Skip to content

Commit f82872a

Browse files
committed
Allow adding tags to AndroidManifest.xml from project.xml. (#1814)
* Reduce indentation in `ConfigData`. * Use more specific return type for `getKeyValueArray()`. * Reserve "config:" prefix when parsing config data. * Use new reserved prefix to avoid collisions. * `ConfigData`: use stored values instead of looking them up repeatedly. Also, there's no need for `Reflect.hasField()`. We know it has the field, because we're iterating over `Reflect.fields()`. * Fix extraneous null values in `ConfigData` arrays. * Simplify control flow. * Allow adding tags to AndroidManifest.xml from project.xml. For instance, `<config:android><manifest><uses-permission android:name="xyz" /></manifest></config:android>` will copy the `uses-permission` tag directly into the manifest. This allows you to specify attributes like `android:maxSdkVersion` that aren't normally available. * Fix `Std.isOfType()` being used in old Haxe versions.
1 parent f0dc0aa commit f82872a

File tree

4 files changed

+141
-98
lines changed

4 files changed

+141
-98
lines changed

src/lime/tools/ConfigData.hx

Lines changed: 120 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ import haxe.xml.Fast as Access;
99

1010
abstract ConfigData(Dynamic) to Dynamic from Dynamic
1111
{
12-
private static inline var ARRAY:String = "___array";
12+
private static inline var ARRAY:String = "config:array_";
13+
14+
/**
15+
If set, `parse()` will add child nodes to this array instead of parsing them.
16+
**/
17+
@:noCompletion public var xmlChildren(get, set):Array<String>;
1318

1419
public function new()
1520
{
@@ -75,7 +80,7 @@ abstract ConfigData(Dynamic) to Dynamic from Dynamic
7580

7681
if (current != null)
7782
{
78-
array = Reflect.field(current, field + ARRAY);
83+
array = Reflect.field(current, ARRAY + field);
7984

8085
if (array == null && Reflect.hasField(current, field))
8186
{
@@ -181,7 +186,7 @@ abstract ConfigData(Dynamic) to Dynamic from Dynamic
181186
var data = get(id);
182187
for (key in Reflect.fields(data))
183188
{
184-
if (!StringTools.endsWith (key, ARRAY))
189+
if (!StringTools.startsWith(key, "config:"))
185190
{
186191
Reflect.setField(values, key, Reflect.field(data, key));
187192
}
@@ -216,72 +221,63 @@ abstract ConfigData(Dynamic) to Dynamic from Dynamic
216221
{
217222
for (field in Reflect.fields(source))
218223
{
219-
if (StringTools.endsWith(field, ARRAY))
224+
if (StringTools.startsWith(field, ARRAY))
220225
{
221226
continue;
222227
}
223228

224-
var doCopy = true;
225-
var exists = Reflect.hasField(destination, field);
226-
var typeDest:String = null;
229+
var valueSource = Reflect.field(source, field);
230+
var valueDest = Reflect.field(destination, field);
231+
var typeSource:String = Type.typeof(valueSource).getName();
232+
var typeDest:String = Type.typeof(valueDest).getName();
227233

228-
if (exists)
234+
// if trying to copy a non object over an object, don't
235+
if (typeSource != "TObject" && typeDest == "TObject")
229236
{
230-
var valueSource = Reflect.field(source, field);
231-
var valueDest = Reflect.field(destination, field);
232-
var typeSource = Type.typeof(valueSource).getName();
233-
typeDest = Type.typeof(valueDest).getName();
237+
continue;
238+
}
234239

235-
// if trying to copy a non object over an object, don't
236-
if (typeSource != "TObject" && typeDest == "TObject")
240+
if (valueSource != valueDest && valueDest != null && typeSource != "TObject" && !#if (haxe_ver >= 4.2) Std.isOfType #else Std.is #end(valueSource, Array))
241+
{
242+
if (!Reflect.hasField(destination, ARRAY + field))
237243
{
238-
doCopy = false;
239-
240-
// if (Log.verbose) {
241-
//
242-
// Log.println (field + " not merged by preference");
243-
//
244-
// }
244+
Reflect.setField(destination, ARRAY + field, [ObjectTools.deepCopy(Reflect.field(destination, field))]);
245245
}
246246

247-
if (doCopy && Reflect.field(source, field) != Reflect.field(destination, field) && typeSource != "TObject")
248-
{
249-
if (!Reflect.hasField(destination, field + ARRAY))
250-
{
251-
Reflect.setField(destination, field + ARRAY, [ObjectTools.deepCopy(Reflect.field(destination, field))]);
252-
}
253-
254-
var array:Array<Dynamic> = Reflect.field(destination, field + ARRAY);
247+
var array:Array<Dynamic> = Reflect.field(destination, ARRAY + field);
255248

256-
if (Reflect.hasField(source, field + ARRAY))
257-
{
258-
array = array.concat(Reflect.field(source, field + ARRAY));
259-
Reflect.setField(destination, field + ARRAY, array);
260-
}
261-
else
262-
{
263-
array.push(Reflect.field(source, field));
264-
}
265-
266-
Reflect.setField(destination, field, Reflect.field(source, field));
267-
doCopy = false;
249+
if (Reflect.hasField(source, ARRAY + field))
250+
{
251+
array = array.concat(Reflect.field(source, ARRAY + field));
252+
Reflect.setField(destination, ARRAY + field, array);
268253
}
254+
else
255+
{
256+
array.push(Reflect.field(source, field));
257+
}
258+
259+
Reflect.setField(destination, field, Reflect.field(source, field));
260+
continue;
269261
}
270262

271-
if (doCopy)
263+
if (typeDest == "TObject")
272264
{
273-
if (typeDest == "TObject")
265+
mergeValues(valueSource, valueDest);
266+
}
267+
else if (typeDest == "TClass" && #if (haxe_ver >= 4.2) Std.isOfType #else Std.is #end (valueSource, Array) && #if (haxe_ver >= 4.2) Std.isOfType #else Std.is #end(valueDest, Array))
268+
{
269+
for (item in (cast valueSource:Array<Dynamic>))
274270
{
275-
mergeValues(Reflect.field(source, field), Reflect.field(destination, field));
271+
(cast valueDest:Array<Dynamic>).push(item);
276272
}
277-
else
278-
{
279-
Reflect.setField(destination, field, Reflect.field(source, field));
273+
}
274+
else
275+
{
276+
Reflect.setField(destination, field, Reflect.field(source, field));
280277

281-
if (Reflect.hasField(source, field + ARRAY))
282-
{
283-
Reflect.setField(destination, field + ARRAY, Reflect.field(source, field + ARRAY));
284-
}
278+
if (Reflect.hasField(source, ARRAY + field))
279+
{
280+
Reflect.setField(destination, ARRAY + field, Reflect.field(source, ARRAY + field));
285281
}
286282
}
287283
}
@@ -302,6 +298,10 @@ abstract ConfigData(Dynamic) to Dynamic from Dynamic
302298
{
303299
bucketType = elem.att.type;
304300
}
301+
else if (elem.x.exists("config:type"))
302+
{
303+
bucketType = elem.x.get("config:type");
304+
}
305305

306306
if (bucketType != "")
307307
{
@@ -318,7 +318,7 @@ abstract ConfigData(Dynamic) to Dynamic from Dynamic
318318
{
319319
for (attrName in elem.x.attributes())
320320
{
321-
if (attrName != "type")
321+
if (attrName != "type" && attrName != "config:type")
322322
{
323323
var attrValue = elem.x.get(attrName);
324324
if (substitute != null) attrValue = substitute(attrValue);
@@ -327,64 +327,77 @@ abstract ConfigData(Dynamic) to Dynamic from Dynamic
327327
}
328328
}
329329

330-
private function parseChildren(elem:Access, bucket:Dynamic, depth:Int = 0, substitute:String->String = null):Void
330+
private function parseChildren(elem:Access, bucket:ConfigData, depth:Int = 0, substitute:String->String = null):Void
331331
{
332+
if (bucket.xmlChildren != null)
333+
{
334+
var children:Array<String> = bucket.xmlChildren;
335+
for (child in elem.elements)
336+
{
337+
children.push(child.x.toString());
338+
}
339+
340+
return;
341+
}
342+
332343
for (child in elem.elements)
333344
{
334-
if (child.name != "config")
345+
if (child.name == "config")
335346
{
336-
// log("config data > child : " + child.name);
347+
continue;
348+
}
337349

338-
var d = depth + 1;
350+
// log("config data > child : " + child.name);
339351

340-
var hasChildren = child.x.elements().hasNext();
341-
var hasAttributes = child.x.attributes().hasNext();
352+
var d = depth + 1;
342353

343-
if (Reflect.hasField(bucket, child.name))
344-
{
345-
var array:Array<Dynamic> = Reflect.field(bucket, child.name + ARRAY);
346-
if (array == null)
347-
{
348-
array = [ObjectTools.deepCopy(Reflect.field(bucket, child.name))];
349-
Reflect.setField(bucket, child.name + ARRAY, array);
350-
}
354+
var hasChildren = child.x.elements().hasNext();
355+
var hasAttributes = child.x.attributes().hasNext();
351356

352-
var arrayBucket = {};
353-
array.push(arrayBucket);
357+
if (Reflect.hasField(bucket, child.name))
358+
{
359+
var array:Array<Dynamic> = Reflect.field(bucket, ARRAY + child.name);
360+
if (array == null)
361+
{
362+
array = [ObjectTools.deepCopy(Reflect.field(bucket, child.name))];
363+
Reflect.setField(bucket, ARRAY + child.name, array);
364+
}
354365

355-
if (hasAttributes)
356-
{
357-
parseAttributes(child, arrayBucket, substitute);
358-
}
366+
var arrayBucket = {};
367+
array.push(arrayBucket);
359368

360-
if (hasChildren)
361-
{
362-
parseChildren(child, arrayBucket, d, substitute);
363-
}
369+
if (hasAttributes)
370+
{
371+
parseAttributes(child, arrayBucket, substitute);
372+
}
364373

365-
if (!hasChildren && !hasAttributes)
366-
{
367-
parseValue(child, arrayBucket, substitute);
368-
}
374+
if (hasChildren)
375+
{
376+
parseChildren(child, arrayBucket, d, substitute);
369377
}
370378

371379
if (!hasChildren && !hasAttributes)
372380
{
373-
parseValue(child, bucket, substitute);
381+
parseValue(child, arrayBucket, substitute);
374382
}
375-
else
376-
{
377-
var childBucket = addBucket(child.name, bucket);
383+
}
378384

379-
if (hasAttributes)
380-
{
381-
parseAttributes(child, childBucket, substitute);
382-
}
385+
if (!hasChildren && !hasAttributes)
386+
{
387+
parseValue(child, bucket, substitute);
388+
}
389+
else
390+
{
391+
var childBucket = addBucket(child.name, bucket);
383392

384-
if (hasChildren)
385-
{
386-
parseChildren(child, childBucket, d, substitute);
387-
}
393+
if (hasAttributes)
394+
{
395+
parseAttributes(child, childBucket, substitute);
396+
}
397+
398+
if (hasChildren)
399+
{
400+
parseChildren(child, childBucket, d, substitute);
388401
}
389402
}
390403
}
@@ -426,12 +439,12 @@ abstract ConfigData(Dynamic) to Dynamic from Dynamic
426439

427440
if (Reflect.hasField(current, field))
428441
{
429-
var array:Array<Dynamic> = Reflect.field(current, field + ARRAY);
442+
var array:Array<Dynamic> = Reflect.field(current, ARRAY + field);
430443

431444
if (array == null)
432445
{
433446
array = [ObjectTools.deepCopy(Reflect.field(current, field))];
434-
Reflect.setField(current, field + ARRAY, array);
447+
Reflect.setField(current, ARRAY + field, array);
435448
}
436449

437450
if (!unique || array.indexOf(value) == -1)
@@ -495,11 +508,11 @@ abstract ConfigData(Dynamic) to Dynamic from Dynamic
495508
{
496509
if (typeSource != "TObject")
497510
{
498-
var array:Array<Dynamic> = Reflect.field(bucket, node + ARRAY);
511+
var array:Array<Dynamic> = Reflect.field(bucket, ARRAY + node);
499512
if (array == null)
500513
{
501514
array = [ObjectTools.deepCopy(Reflect.field(bucket, node))];
502-
Reflect.setField(bucket, node + ARRAY, array);
515+
Reflect.setField(bucket, ARRAY + node, array);
503516
}
504517

505518
array.push(value);
@@ -513,4 +526,15 @@ abstract ConfigData(Dynamic) to Dynamic from Dynamic
513526
Reflect.setField(bucket, node, value);
514527
}
515528
}
529+
530+
// Getters & Setters
531+
532+
private inline function get_xmlChildren():Array<String> {
533+
return Reflect.field(this, "config:xml_children");
534+
}
535+
536+
private inline function set_xmlChildren(value:Array<String>):Array<String> {
537+
Reflect.setField(this, "config:xml_children", value);
538+
return value;
539+
}
516540
}

src/lime/tools/HXProject.hx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,11 @@ class HXProject extends Script
212212
splashScreens = new Array<SplashScreen>();
213213
targetHandlers = new Map<String, String>();
214214

215+
config.set("android", { manifest:{}, application:{}, activity:{} });
216+
config.get("android.manifest").xmlChildren = [];
217+
config.get("android.application").xmlChildren = [];
218+
config.get("android.activity").xmlChildren = [];
219+
215220
initializeDefines();
216221
}
217222

templates/android/template/app/src/main/AndroidManifest.xml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="::APP_BUILD_NUMBER::" android:versionName="::APP_VERSION::" android:installLocation="::ANDROID_INSTALL_LOCATION::">
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android" ::foreach ANDROID_MANIFEST::::if ((value != null) && (value != ""))::::key::="::value::" ::end::::end::>
33

44
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
55
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
66

77
::foreach ANDROID_PERMISSIONS::<uses-permission android:name="::__current__::" />
88
::end::
9+
::foreach ANDROID_MANIFEST_CHILDREN::::__current__::
10+
::end::
911

1012
<application ::foreach ANDROID_APPLICATION::::if ((value != null) && (value != ""))::::key::="::value::" ::end::::end::>
1113

@@ -14,6 +16,8 @@
1416
::elseif (WIN_ORIENTATION=="landscape")::
1517
<meta-data android:name="SDL_ENV.SDL_IOS_ORIENTATIONS" android:value="LandscapeLeft LandscapeRight" />
1618
::end::
19+
::foreach ANDROID_APPLICATION_CHILDREN::::__current__::
20+
::end::
1721

1822
<activity ::foreach ANDROID_ACTIVITY::::if ((value != null) && (value != ""))::::key::="::value::" ::end::::end::>
1923

@@ -35,6 +39,9 @@
3539
</intent-filter>
3640
::end::
3741

42+
::foreach ANDROID_ACTIVITY_CHILDREN::::__current__::
43+
::end::
44+
3845
</activity>
3946

4047
</application>

0 commit comments

Comments
 (0)