mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-08 04:20:09 +00:00
Projucer: Support file permissions in Android 33
This commit is contained in:
parent
2dc90bd6e6
commit
e3e8b8a91d
4 changed files with 129 additions and 61 deletions
|
|
@ -71,6 +71,19 @@ public:
|
|||
createOtherExporterProperties (props);
|
||||
}
|
||||
|
||||
void updateDeprecatedSettings() override
|
||||
{
|
||||
const auto needsExternalRead = getSettingString (Ids::androidExternalReadNeeded);
|
||||
settings.removeProperty (Ids::androidExternalReadNeeded, nullptr);
|
||||
|
||||
if (needsExternalRead.isEmpty())
|
||||
return;
|
||||
|
||||
androidReadMediaAudioPermission .setValue (needsExternalRead, nullptr);
|
||||
androidReadMediaImagesPermission.setValue (needsExternalRead, nullptr);
|
||||
androidReadMediaVideoPermission .setValue (needsExternalRead, nullptr);
|
||||
}
|
||||
|
||||
static String getDisplayName() { return "Android"; }
|
||||
static String getValueTreeTypeName() { return "ANDROIDSTUDIO"; }
|
||||
static String getTargetFolderName() { return "Android"; }
|
||||
|
|
@ -94,7 +107,8 @@ public:
|
|||
androidCustomActivityClass, androidCustomApplicationClass, androidManifestCustomXmlElements,
|
||||
androidGradleSettingsContent, androidVersionCode, androidMinimumSDK, androidTargetSDK, androidTheme,
|
||||
androidExtraAssetsFolder, androidOboeRepositoryPath, androidInternetNeeded, androidMicNeeded, androidCameraNeeded,
|
||||
androidBluetoothNeeded, androidExternalReadPermission, androidExternalWritePermission,
|
||||
androidBluetoothNeeded, androidReadMediaAudioPermission, androidReadMediaImagesPermission,
|
||||
androidReadMediaVideoPermission, androidExternalWritePermission,
|
||||
androidInAppBillingPermission, androidVibratePermission, androidOtherPermissions, androidPushNotifications,
|
||||
androidEnableRemoteNotifications, androidRemoteNotificationsConfigFile, androidEnableContentSharing, androidKeyStore,
|
||||
androidKeyStorePass, androidKeyAlias, androidKeyAliasPass, gradleVersion, gradleToolchain, androidPluginVersion;
|
||||
|
|
@ -124,7 +138,9 @@ public:
|
|||
androidMicNeeded (settings, Ids::microphonePermissionNeeded, getUndoManager(), false),
|
||||
androidCameraNeeded (settings, Ids::cameraPermissionNeeded, getUndoManager(), false),
|
||||
androidBluetoothNeeded (settings, Ids::androidBluetoothNeeded, getUndoManager(), true),
|
||||
androidExternalReadPermission (settings, Ids::androidExternalReadNeeded, getUndoManager(), true),
|
||||
androidReadMediaAudioPermission (settings, Ids::androidReadMediaAudioPermission, getUndoManager(), true),
|
||||
androidReadMediaImagesPermission (settings, Ids::androidReadMediaImagesPermission, getUndoManager(), true),
|
||||
androidReadMediaVideoPermission (settings, Ids::androidReadMediaVideoPermission, getUndoManager(), true),
|
||||
androidExternalWritePermission (settings, Ids::androidExternalWriteNeeded, getUndoManager(), true),
|
||||
androidInAppBillingPermission (settings, Ids::androidInAppBilling, getUndoManager(), false),
|
||||
androidVibratePermission (settings, Ids::androidVibratePermissionNeeded, getUndoManager(), false),
|
||||
|
|
@ -1103,8 +1119,14 @@ private:
|
|||
props.add (new ChoicePropertyComponent (androidBluetoothNeeded, "Bluetooth Permissions Required"),
|
||||
"If enabled, this will set the android.permission.BLUETOOTH and android.permission.BLUETOOTH_ADMIN flag in the manifest. This is required for Bluetooth MIDI on Android.");
|
||||
|
||||
props.add (new ChoicePropertyComponent (androidExternalReadPermission, "Read From External Storage"),
|
||||
"If enabled, this will set the android.permission.READ_EXTERNAL_STORAGE flag in the manifest.");
|
||||
props.add (new ChoicePropertyComponent (androidReadMediaAudioPermission, "Read Audio From External Storage"),
|
||||
"If enabled, this will set the android.permission.READ_MEDIA_AUDIO and android.permission.READ_EXTERNAL_STORAGE flags in the manifest.");
|
||||
|
||||
props.add (new ChoicePropertyComponent (androidReadMediaImagesPermission, "Read Images From External Storage"),
|
||||
"If enabled, this will set the android.permission.READ_MEDIA_IMAGES and android.permission.READ_EXTERNAL_STORAGE flags in the manifest.");
|
||||
|
||||
props.add (new ChoicePropertyComponent (androidReadMediaVideoPermission, "Read Video From External Storage"),
|
||||
"If enabled, this will set the android.permission.READ_MEDIA_VIDEO and android.permission.READ_EXTERNAL_STORAGE flags in the manifest.");
|
||||
|
||||
props.add (new ChoicePropertyComponent (androidExternalWritePermission, "Write to External Storage"),
|
||||
"If enabled, this will set the android.permission.WRITE_EXTERNAL_STORAGE flag in the manifest.");
|
||||
|
|
@ -1680,6 +1702,15 @@ private:
|
|||
// This permission only has an effect on SDK version 28 and lower
|
||||
if (permission == "android.permission.WRITE_EXTERNAL_STORAGE")
|
||||
usesPermission->setAttribute ("android:maxSdkVersion", "28");
|
||||
|
||||
// https://developer.android.com/training/data-storage/shared/documents-files
|
||||
// If the SDK version is <= 28, READ_EXTERNAL_STORAGE is required to access any
|
||||
// media file, including files created by the current app.
|
||||
// If the SDK version is <= 32, READ_EXTERNAL_STORAGE is required to access other
|
||||
// apps' media files.
|
||||
// This permission has no effect on later Android versions.
|
||||
if (permission == "android.permission.READ_EXTERNAL_STORAGE")
|
||||
usesPermission->setAttribute ("android:maxSdkVersion", "32");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1845,7 +1876,18 @@ private:
|
|||
s.add ("android.permission.ACCESS_COARSE_LOCATION");
|
||||
}
|
||||
|
||||
if (androidExternalReadPermission.get())
|
||||
if (androidReadMediaAudioPermission.get())
|
||||
s.add ("android.permission.READ_MEDIA_AUDIO");
|
||||
|
||||
if (androidReadMediaImagesPermission.get())
|
||||
s.add ("android.permission.READ_MEDIA_IMAGES");
|
||||
|
||||
if (androidReadMediaVideoPermission.get())
|
||||
s.add ("android.permission.READ_MEDIA_VIDEO");
|
||||
|
||||
if ( androidReadMediaAudioPermission.get()
|
||||
|| androidReadMediaImagesPermission.get()
|
||||
|| androidReadMediaVideoPermission.get())
|
||||
s.add ("android.permission.READ_EXTERNAL_STORAGE");
|
||||
|
||||
if (androidExternalWritePermission.get())
|
||||
|
|
|
|||
|
|
@ -235,6 +235,9 @@ namespace Ids
|
|||
DECLARE_ID (androidCustomStringXmlElements);
|
||||
DECLARE_ID (androidBluetoothNeeded);
|
||||
DECLARE_ID (androidExternalReadNeeded);
|
||||
DECLARE_ID (androidReadMediaAudioPermission);
|
||||
DECLARE_ID (androidReadMediaImagesPermission);
|
||||
DECLARE_ID (androidReadMediaVideoPermission);
|
||||
DECLARE_ID (androidExternalWriteNeeded);
|
||||
DECLARE_ID (androidInAppBilling);
|
||||
DECLARE_ID (androidVibratePermissionNeeded);
|
||||
|
|
|
|||
|
|
@ -86,7 +86,22 @@ public:
|
|||
writeExternalStorage = 4,
|
||||
|
||||
/** Permission to use camera */
|
||||
camera = 5
|
||||
camera = 5,
|
||||
|
||||
/** Permission to read audio files that your app didn't create.
|
||||
Has the same effect as readExternalStorage on iOS and Android versions before 33.
|
||||
*/
|
||||
readMediaAudio = 6,
|
||||
|
||||
/** Permission to read image files that your app didn't create.
|
||||
Has the same effect as readExternalStorage on iOS and Android versions before 33.
|
||||
*/
|
||||
readMediaImages = 7,
|
||||
|
||||
/** Permission to read video files that your app didn't create.
|
||||
Has the same effect as readExternalStorage on iOS and Android versions before 33.
|
||||
*/
|
||||
readMediaVideo = 8
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -24,15 +24,39 @@ namespace juce
|
|||
{
|
||||
|
||||
//==============================================================================
|
||||
static String jucePermissionToAndroidPermission (RuntimePermissions::PermissionID permission)
|
||||
static StringArray jucePermissionToAndroidPermissions (RuntimePermissions::PermissionID permission)
|
||||
{
|
||||
const auto externalStorageOrMedia = [] (const auto* newPermission)
|
||||
{
|
||||
return getAndroidSDKVersion() < 33 ? "android.permission.READ_EXTERNAL_STORAGE" : newPermission;
|
||||
};
|
||||
|
||||
switch (permission)
|
||||
{
|
||||
case RuntimePermissions::recordAudio: return "android.permission.RECORD_AUDIO";
|
||||
case RuntimePermissions::bluetoothMidi: return "android.permission.ACCESS_FINE_LOCATION";
|
||||
case RuntimePermissions::readExternalStorage: return "android.permission.READ_EXTERNAL_STORAGE";
|
||||
case RuntimePermissions::writeExternalStorage: return "android.permission.WRITE_EXTERNAL_STORAGE";
|
||||
case RuntimePermissions::camera: return "android.permission.CAMERA";
|
||||
case RuntimePermissions::recordAudio: return { "android.permission.RECORD_AUDIO" };
|
||||
case RuntimePermissions::bluetoothMidi: return { "android.permission.ACCESS_FINE_LOCATION" };
|
||||
case RuntimePermissions::writeExternalStorage: return { "android.permission.WRITE_EXTERNAL_STORAGE" };
|
||||
case RuntimePermissions::camera: return { "android.permission.CAMERA" };
|
||||
|
||||
case RuntimePermissions::readExternalStorage:
|
||||
{
|
||||
// See: https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE
|
||||
if (getAndroidSDKVersion() < 33)
|
||||
return { "android.permission.READ_EXTERNAL_STORAGE" };
|
||||
|
||||
return { "android.permission.READ_MEDIA_AUDIO",
|
||||
"android.permission.READ_MEDIA_IMAGES",
|
||||
"android.permission.READ_MEDIA_VIDEO" };
|
||||
}
|
||||
|
||||
case RuntimePermissions::readMediaAudio:
|
||||
return { externalStorageOrMedia ("android.permission.READ_MEDIA_AUDIO") };
|
||||
|
||||
case RuntimePermissions::readMediaImages:
|
||||
return { externalStorageOrMedia ("android.permission.READ_MEDIA_IMAGES") };
|
||||
|
||||
case RuntimePermissions::readMediaVideo:
|
||||
return { externalStorageOrMedia ("android.permission.READ_MEDIA_VIDEO") };
|
||||
}
|
||||
|
||||
// invalid permission
|
||||
|
|
@ -42,53 +66,28 @@ static String jucePermissionToAndroidPermission (RuntimePermissions::PermissionI
|
|||
|
||||
static RuntimePermissions::PermissionID androidPermissionToJucePermission (const String& permission)
|
||||
{
|
||||
if (permission == "android.permission.RECORD_AUDIO") return RuntimePermissions::recordAudio;
|
||||
else if (permission == "android.permission.ACCESS_FINE_LOCATION") return RuntimePermissions::bluetoothMidi;
|
||||
else if (permission == "android.permission.READ_EXTERNAL_STORAGE") return RuntimePermissions::readExternalStorage;
|
||||
else if (permission == "android.permission.WRITE_EXTERNAL_STORAGE") return RuntimePermissions::writeExternalStorage;
|
||||
else if (permission == "android.permission.CAMERA") return RuntimePermissions::camera;
|
||||
static const std::map<String, RuntimePermissions::PermissionID> map
|
||||
{
|
||||
{ "android.permission.RECORD_AUDIO", RuntimePermissions::recordAudio },
|
||||
{ "android.permission.ACCESS_FINE_LOCATION", RuntimePermissions::bluetoothMidi },
|
||||
{ "android.permission.READ_EXTERNAL_STORAGE", RuntimePermissions::readExternalStorage },
|
||||
{ "android.permission.WRITE_EXTERNAL_STORAGE", RuntimePermissions::writeExternalStorage },
|
||||
{ "android.permission.CAMERA", RuntimePermissions::camera },
|
||||
{ "android.permission.READ_MEDIA_AUDIO", RuntimePermissions::readMediaAudio },
|
||||
{ "android.permission.READ_MEDIA_IMAGES", RuntimePermissions::readMediaImages },
|
||||
{ "android.permission.READ_MEDIA_VIDEO", RuntimePermissions::readMediaVideo },
|
||||
};
|
||||
|
||||
return static_cast<RuntimePermissions::PermissionID> (-1);
|
||||
const auto iter = map.find (permission);
|
||||
return iter != map.cend() ? iter->second
|
||||
: static_cast<RuntimePermissions::PermissionID> (-1);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct PermissionsRequest
|
||||
{
|
||||
PermissionsRequest() {}
|
||||
|
||||
// using "= default" on the following method triggers an internal compiler error
|
||||
// in Android NDK 17
|
||||
PermissionsRequest (const PermissionsRequest& o)
|
||||
: callback (o.callback), permission (o.permission)
|
||||
{}
|
||||
|
||||
PermissionsRequest (PermissionsRequest&& o)
|
||||
: callback (std::move (o.callback)), permission (o.permission)
|
||||
{
|
||||
o.permission = static_cast<RuntimePermissions::PermissionID> (-1);
|
||||
}
|
||||
|
||||
PermissionsRequest (RuntimePermissions::Callback && callbackToUse,
|
||||
RuntimePermissions::PermissionID permissionToRequest)
|
||||
: callback (std::move (callbackToUse)), permission (permissionToRequest)
|
||||
{}
|
||||
|
||||
PermissionsRequest& operator= (const PermissionsRequest & o)
|
||||
{
|
||||
callback = o.callback;
|
||||
permission = o.permission;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PermissionsRequest& operator= (PermissionsRequest && o)
|
||||
{
|
||||
callback = std::move (o.callback);
|
||||
permission = o.permission;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RuntimePermissions::Callback callback;
|
||||
RuntimePermissions::PermissionID permission;
|
||||
RuntimePermissions::PermissionID permission = static_cast<RuntimePermissions::PermissionID> (-1);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -165,8 +164,7 @@ struct PermissionsOverlay : FragmentOverlay
|
|||
{
|
||||
auto &request = requests.front();
|
||||
|
||||
StringArray permissionsArray{
|
||||
jucePermissionToAndroidPermission (request.permission)};
|
||||
auto permissionsArray = jucePermissionToAndroidPermissions (request.permission);
|
||||
auto jPermissionsArray = juceStringArrayToJava (permissionsArray);
|
||||
|
||||
|
||||
|
|
@ -199,9 +197,16 @@ struct PermissionsOverlay : FragmentOverlay
|
|||
//==============================================================================
|
||||
void RuntimePermissions::request (PermissionID permission, Callback callback)
|
||||
{
|
||||
auto requestedPermission = jucePermissionToAndroidPermission (permission);
|
||||
const auto requestedPermissions = jucePermissionToAndroidPermissions (permission);
|
||||
|
||||
if (! isPermissionDeclaredInManifest (requestedPermission))
|
||||
const auto allPermissionsInManifest = std::all_of (requestedPermissions.begin(),
|
||||
requestedPermissions.end(),
|
||||
[] (const auto& p)
|
||||
{
|
||||
return isPermissionDeclaredInManifest (p);
|
||||
});
|
||||
|
||||
if (! allPermissionsInManifest)
|
||||
{
|
||||
// Error! If you want to be able to request this runtime permission, you
|
||||
// also need to declare it in your app's manifest. You can do so via
|
||||
|
|
@ -220,7 +225,7 @@ void RuntimePermissions::request (PermissionID permission, Callback callback)
|
|||
return;
|
||||
}
|
||||
|
||||
PermissionsRequest request (std::move (callback), permission);
|
||||
PermissionsRequest request { std::move (callback), permission };
|
||||
|
||||
static CriticalSection overlayGuard;
|
||||
ScopedLock lock (overlayGuard);
|
||||
|
|
@ -250,12 +255,15 @@ bool RuntimePermissions::isGranted (PermissionID permission)
|
|||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
auto requestedPermission = jucePermissionToAndroidPermission (permission);
|
||||
int result = env->CallIntMethod (getAppContext().get(), AndroidContext.checkCallingOrSelfPermission,
|
||||
javaString (requestedPermission).get());
|
||||
const auto requestedPermissions = jucePermissionToAndroidPermissions (permission);
|
||||
|
||||
return std::all_of (requestedPermissions.begin(), requestedPermissions.end(), [env] (const auto& p)
|
||||
{
|
||||
return 0 == env->CallIntMethod (getAppContext().get(),
|
||||
AndroidContext.checkCallingOrSelfPermission,
|
||||
javaString (p).get());
|
||||
});
|
||||
|
||||
return result == 0 /* PERMISSION_GRANTED */;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue