1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-09 23:34:20 +00:00

Projucer: Add manifest option to allow virtual MIDI on Android

This commit is contained in:
reuk 2025-03-19 14:55:06 +00:00
parent 1c651e962b
commit 57ff869db0
No known key found for this signature in database
2 changed files with 104 additions and 2 deletions

View file

@ -34,7 +34,6 @@
#pragma once
//==============================================================================
class AndroidProjectExporter final : public ProjectExporter
{
@ -113,7 +112,8 @@ public:
androidReadMediaVideoPermission, androidExternalWritePermission,
androidInAppBillingPermission, androidVibratePermission, androidOtherPermissions, androidPushNotifications,
androidEnableRemoteNotifications, androidRemoteNotificationsConfigFile, androidEnableContentSharing, androidKeyStore,
androidKeyStorePass, androidKeyAlias, androidKeyAliasPass, gradleVersion, gradleToolchain, gradleClangTidy, androidPluginVersion;
androidKeyStorePass, androidKeyAlias, androidKeyAliasPass, gradleVersion, gradleToolchain, gradleClangTidy, androidPluginVersion,
androidEnableVirtualMidi;
//==============================================================================
AndroidProjectExporter (Project& p, const ValueTree& t)
@ -161,6 +161,7 @@ public:
gradleToolchain (settings, Ids::gradleToolchain, getUndoManager(), "clang"),
gradleClangTidy (settings, Ids::gradleClangTidy, getUndoManager(), false),
androidPluginVersion (settings, Ids::androidPluginVersion, getUndoManager(), "8.10.0"),
androidEnableVirtualMidi (settings, Ids::androidEnableVirtualMidi, getUndoManager(), false),
AndroidExecutable (getAppSettings().getStoredPath (Ids::androidStudioExePath, TargetOS::getThisOS()).get().toString())
{
name = getDisplayName();
@ -235,6 +236,7 @@ public:
{
copyAdditionalJavaLibs (appFolder);
writeStringsXML (targetFolder);
writeDeviceInfoXML (targetFolder);
writeAppIcons (targetFolder);
}
@ -999,6 +1001,9 @@ private:
if (isInAppBillingEnabled())
addOptJavaFolderToSourceSetsForModule (javaSourceSets, modules, "juce_product_unlocking");
if (isVirtualMidiEnabled())
addOptJavaFolderToSourceSetsForModule (javaSourceSets, modules, "juce_audio_devices");
MemoryOutputStream mo;
mo.setNewLineString (getNewLineString());
@ -1215,6 +1220,11 @@ private:
props.add (new TextPropertyComponent (androidRemoteNotificationsConfigFile.getPropertyAsValue(), "Remote Notifications Config File", 2048, false),
"Path to google-services.json file. This will be the file provided by Firebase when creating a new app in Firebase console.");
props.add (new ChoicePropertyComponent (androidEnableVirtualMidi, "Enable Virtual MIDI"),
"When enabled, this will add entries to your application manifest declaring that your program "
"can provide the MidiDeviceService and/or MidiUmpDeviceService."
"This has no effect unless the juce_audio_devices module is included in the project.");
props.add (new TextPropertyComponent (androidManifestCustomXmlElements, "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 "
@ -1341,6 +1351,12 @@ private:
&& androidEnableRemoteNotifications.get();
}
bool isVirtualMidiEnabled() const
{
return project.getEnabledModules().isModuleEnabled ("juce_audio_devices")
&& androidEnableVirtualMidi.get();
}
bool isInAppBillingEnabled() const
{
return project.getEnabledModules().isModuleEnabled ("juce_product_unlocking")
@ -1395,6 +1411,58 @@ private:
}
}
void writeDeviceInfoXML (const File& folder) const
{
const auto makeDeviceNode = [this] (XmlElement& devices)
{
auto* device = devices.createNewChildElement ("device");
device->setAttribute ("manufacturer", project.getCompanyNameString());
device->setAttribute ("product", projectName);
const auto deviceName = (project.getCompanyNameString().isNotEmpty() ? (project.getCompanyNameString() + " ") : "")
+ projectName;
device->setAttribute ("name", deviceName);
return device;
};
if (isVirtualMidiEnabled())
{
{
auto path = folder.getChildFile ("app")
.getChildFile ("src")
.getChildFile ("main")
.getChildFile ("res")
.getChildFile ("xml")
.getChildFile ("juce_midi_virtual_ump.xml");
auto devices = std::make_unique<XmlElement> ("devices");
auto* device = makeDeviceNode (*devices);
auto* port = device->createNewChildElement ("port");
port->setAttribute ("name", "MIDI 2.0");
writeXmlOrThrow (*devices, path, "utf-8", 100, true);
}
{
auto path = folder.getChildFile ("app")
.getChildFile ("src")
.getChildFile ("main")
.getChildFile ("res")
.getChildFile ("xml")
.getChildFile ("juce_midi_virtual_bytestream.xml");
auto devices = std::make_unique<XmlElement> ("devices");
auto* device = makeDeviceNode (*devices);
auto* portIn = device->createNewChildElement ("input-port");
portIn->setAttribute ("name", "In");
auto* portOut = device->createNewChildElement ("output-port");
portOut->setAttribute ("name", "Out");
writeXmlOrThrow (*devices, path, "utf-8", 100, true);
}
}
}
void writeAndroidManifest (const File& folder) const
{
std::unique_ptr<XmlElement> manifest (createManifestXML());
@ -1898,6 +1966,39 @@ private:
metaData->setAttribute ("android:name", "firebase_analytics_collection_deactivated");
metaData->setAttribute ("android:value", "true");
}
if (isVirtualMidiEnabled())
{
{
auto* service = application.createNewChildElement ("service");
service->setAttribute ("android:name", "com.rmsl.juce.VirtualMidiServices$VirtualUmpService");
service->setAttribute ("android:enabled", "false");
service->setAttribute ("android:exported", "true");
service->setAttribute ("android:permission", "android.permission.BIND_MIDI_DEVICE_SERVICE");
auto* intentFilter = service->createNewChildElement ("intent-filter");
intentFilter->createNewChildElement ("action")->setAttribute ("android:name", "android.media.midi.MidiUmpDeviceService");
auto* property = service->createNewChildElement ("property");
property->setAttribute ("android:name", "android.media.midi.MidiUmpDeviceService");
property->setAttribute ("android:resource", "@xml/juce_midi_virtual_ump");
}
{
auto* service = application.createNewChildElement ("service");
service->setAttribute ("android:name", "com.rmsl.juce.VirtualMidiServices$VirtualBytestreamService");
service->setAttribute ("android:enabled", "false");
service->setAttribute ("android:exported", "true");
service->setAttribute ("android:permission", "android.permission.BIND_MIDI_DEVICE_SERVICE");
auto* intentFilter = service->createNewChildElement ("intent-filter");
intentFilter->createNewChildElement ("action")->setAttribute ("android:name", "android.media.midi.MidiDeviceService");
auto* metadata = service->createNewChildElement ("meta-data");
metadata->setAttribute ("android:name", "android.media.midi.MidiDeviceService");
metadata->setAttribute ("android:resource", "@xml/juce_midi_virtual_bytestream");
}
}
}
void createProviderElement (XmlElement& application) const

View file

@ -268,6 +268,7 @@ namespace Ids
DECLARE_ID (androidScreenOrientation);
DECLARE_ID (androidExtraAssetsFolder);
DECLARE_ID (androidStudioExePath);
DECLARE_ID (androidEnableVirtualMidi);
DECLARE_ID (iosDeviceFamily);
const Identifier iPhoneScreenOrientation ("iosScreenOrientation"); // old name is confusing
DECLARE_ID (iPadScreenOrientation);