diff --git a/docs/android.services.md b/docs/android.services.md new file mode 100644 index 000000000..429cd2a62 --- /dev/null +++ b/docs/android.services.md @@ -0,0 +1,33 @@ +A name-value pair for an item of additional, arbitrary data that can be supplied to the application. + +Equivalent to +[``](https://developer.android.com/guide/topics/manifest/service-element). + +Example: + +```xml + + + +``` + +becomes + +```json +{ + "android": { + "services": [ + { + "android:name": "com.example.locationService", + "android:exported": true, + "android:foregroundServiceType": "location" + } + ], + } +} +``` + +
diff --git a/packages/app/android/android-manifest.js b/packages/app/android/android-manifest.js index 7bb2014a6..40f08788f 100644 --- a/packages/app/android/android-manifest.js +++ b/packages/app/android/android-manifest.js @@ -109,6 +109,31 @@ function generateAndroidManifest(appManifestPath, manifestOutput, fs = nodefs) { } } + // https://developer.android.com/guide/topics/manifest/service-element + const services = android.services; + if (Array.isArray(services)) { + const names = ["android:name"]; + const attributes = [ + "android:description", + "android:directBootAware", + "android:enabled", + "android:exported", + "android:foregroundServiceType", + "android:icon", + "android:isolatedProcess", + "android:label", + "android:name", + "android:permission", + "android:process", + "android:stopWithTask", + ]; + const entries = toXML(services, names, attributes, attributeNamePrefix); + + if (entries.length > 0) { + manifest.application["service"] = entries; + } + } + const builder = new XMLBuilder(xmlOptions); fs.mkdirSync(path.dirname(manifestOutput), { recursive: true, mode: 0o755 }); fs.writeFileSync(manifestOutput, builder.build(xml)); diff --git a/packages/app/scripts/types.ts b/packages/app/scripts/types.ts index 0ceee097c..766676388 100644 --- a/packages/app/scripts/types.ts +++ b/packages/app/scripts/types.ts @@ -29,6 +29,33 @@ export type AndroidConfig = { "android:name": string; "android:value": string; }[]; + services?: { + "android:name": string; + "android:description": string; + "android:directBootAware": "true" | "false"; + "android:enabled": "true" | "false"; + "android:exported": "true" | "false"; + "android:foregroundServiceType": + | "camera" + | "connectedDevice" + | "dataSync" + | "health" + | "location" + | "mediaPlayback" + | "mediaProjection" + | "microphone" + | "phoneCall" + | "remoteMessaging" + | "shortService" + | "specialUse" + | "systemExempted"; + "android:icon": string; + "android:isolatedProcess": "true" | "false"; + "android:label": string; + "android:permission": string; + "android:process": string; + "android:stopWithTask": "true" | "false"; + }[]; }; }; @@ -37,6 +64,7 @@ export type AndroidManifest = { "uses-permission": Record[]; application: { "meta-data": Record[]; + service: Record[]; }; };