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 #pragma once
//============================================================================== //==============================================================================
class AndroidProjectExporter final : public ProjectExporter class AndroidProjectExporter final : public ProjectExporter
{ {
@ -113,7 +112,8 @@ public:
androidReadMediaVideoPermission, androidExternalWritePermission, androidReadMediaVideoPermission, androidExternalWritePermission,
androidInAppBillingPermission, androidVibratePermission, androidOtherPermissions, androidPushNotifications, androidInAppBillingPermission, androidVibratePermission, androidOtherPermissions, androidPushNotifications,
androidEnableRemoteNotifications, androidRemoteNotificationsConfigFile, androidEnableContentSharing, androidKeyStore, 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) AndroidProjectExporter (Project& p, const ValueTree& t)
@ -161,6 +161,7 @@ public:
gradleToolchain (settings, Ids::gradleToolchain, getUndoManager(), "clang"), gradleToolchain (settings, Ids::gradleToolchain, getUndoManager(), "clang"),
gradleClangTidy (settings, Ids::gradleClangTidy, getUndoManager(), false), gradleClangTidy (settings, Ids::gradleClangTidy, getUndoManager(), false),
androidPluginVersion (settings, Ids::androidPluginVersion, getUndoManager(), "8.10.0"), androidPluginVersion (settings, Ids::androidPluginVersion, getUndoManager(), "8.10.0"),
androidEnableVirtualMidi (settings, Ids::androidEnableVirtualMidi, getUndoManager(), false),
AndroidExecutable (getAppSettings().getStoredPath (Ids::androidStudioExePath, TargetOS::getThisOS()).get().toString()) AndroidExecutable (getAppSettings().getStoredPath (Ids::androidStudioExePath, TargetOS::getThisOS()).get().toString())
{ {
name = getDisplayName(); name = getDisplayName();
@ -235,6 +236,7 @@ public:
{ {
copyAdditionalJavaLibs (appFolder); copyAdditionalJavaLibs (appFolder);
writeStringsXML (targetFolder); writeStringsXML (targetFolder);
writeDeviceInfoXML (targetFolder);
writeAppIcons (targetFolder); writeAppIcons (targetFolder);
} }
@ -999,6 +1001,9 @@ private:
if (isInAppBillingEnabled()) if (isInAppBillingEnabled())
addOptJavaFolderToSourceSetsForModule (javaSourceSets, modules, "juce_product_unlocking"); addOptJavaFolderToSourceSetsForModule (javaSourceSets, modules, "juce_product_unlocking");
if (isVirtualMidiEnabled())
addOptJavaFolderToSourceSetsForModule (javaSourceSets, modules, "juce_audio_devices");
MemoryOutputStream mo; MemoryOutputStream mo;
mo.setNewLineString (getNewLineString()); mo.setNewLineString (getNewLineString());
@ -1215,6 +1220,11 @@ private:
props.add (new TextPropertyComponent (androidRemoteNotificationsConfigFile.getPropertyAsValue(), "Remote Notifications Config File", 2048, false), 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."); "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), 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. " "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 " "Projucer will automatically create any missing and required XML elements and attributes "
@ -1341,6 +1351,12 @@ private:
&& androidEnableRemoteNotifications.get(); && androidEnableRemoteNotifications.get();
} }
bool isVirtualMidiEnabled() const
{
return project.getEnabledModules().isModuleEnabled ("juce_audio_devices")
&& androidEnableVirtualMidi.get();
}
bool isInAppBillingEnabled() const bool isInAppBillingEnabled() const
{ {
return project.getEnabledModules().isModuleEnabled ("juce_product_unlocking") 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 void writeAndroidManifest (const File& folder) const
{ {
std::unique_ptr<XmlElement> manifest (createManifestXML()); std::unique_ptr<XmlElement> manifest (createManifestXML());
@ -1898,6 +1966,39 @@ private:
metaData->setAttribute ("android:name", "firebase_analytics_collection_deactivated"); metaData->setAttribute ("android:name", "firebase_analytics_collection_deactivated");
metaData->setAttribute ("android:value", "true"); 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 void createProviderElement (XmlElement& application) const

View file

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