使用适用于 Android 13 的 Cordova 12 在 AndroidManifest 中复制 WRITE_EXTERNAL_STORAGE 权限

jme*_*vin 6 android-manifest cordova android-permissions cordova-plugin-camera

介绍使用 Cordova 12 的应用程序,该应用程序需要目标 SDK Android 13 / API 33。我的应用程序依赖于以下插件(以及许多其他插件)...

cordova-plugin-camera
cordova-plugin-media-capture
Run Code Online (Sandbox Code Playgroud)

这两个插件都会在 AndroidManifest.xml 中插入权限。升级到 Cordova 12 并将 targetSdk 设置为 33 后,构建尝试合并权限失败...

> Task :app:processReleaseMainManifest FAILED
/Users/jmelvin/dev/sizzlescene/repos/mobile/platforms/android/app/src/main/AndroidManifest.xml:47:5-108 Error:
        Element uses-permission#android.permission.WRITE_EXTERNAL_STORAGE at AndroidManifest.xml:47:5-108 duplicated with element declared at AndroidManifest.xml:26:5-81
Run Code Online (Sandbox Code Playgroud)

以下是 cordova-plugin-camera 插件插入的属性...

11a12,14
>         <provider android:authorities="${applicationId}.cordova.plugin.camera.provider" android:exported="false" android:grantUriPermissions="true" android:name="org.apache.cordova.camera.FileProvider">
>             <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/camera_provider_paths" />
>         </provider>
22a26,41
>     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
>     <queries>
>         <intent>
>             <action android:name="android.media.action.IMAGE_CAPTURE" />
>         </intent>
>         <intent>
>             <action android:name="android.intent.action.GET_CONTENT" />
>         </intent>
>         <intent>
>             <action android:name="android.intent.action.PICK" />
>         </intent>
>         <intent>
>             <action android:name="com.android.camera.action.CROP" />
>             <data android:mimeType="image/*" android:scheme="content" />
>         </intent>
>     </queries>
Run Code Online (Sandbox Code Playgroud)

以下是 cordova-plugin-media-capture 插件添加的属性...

22a23,28
>     <uses-permission android:name="android.permission.RECORD_AUDIO" />
>     <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
>     <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
>     <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
>     <uses-permission android:maxSdkVersion="32" android:name="android.permission.READ_EXTERNAL_STORAGE" />
>     <uses-permission android:maxSdkVersion="32" android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Run Code Online (Sandbox Code Playgroud)

最后,当两个插件都包含在构建中时......

11a12,14
>         <provider android:authorities="${applicationId}.cordova.plugin.camera.provider" android:exported="false" android:grantUriPermissions="true" android:name="org.apache.cordova.camera.FileProvider">
>             <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/camera_provider_paths" />
>         </provider>
22a26,47
>     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
>     <queries>
>         <intent>
>             <action android:name="android.media.action.IMAGE_CAPTURE" />
>         </intent>
>         <intent>
>             <action android:name="android.intent.action.GET_CONTENT" />
>         </intent>
>         <intent>
>             <action android:name="android.intent.action.PICK" />
>         </intent>
>         <intent>
>             <action android:name="com.android.camera.action.CROP" />
>             <data android:mimeType="image/*" android:scheme="content" />
>         </intent>
>     </queries>
>     <uses-permission android:name="android.permission.RECORD_AUDIO" />
>     <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
>     <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
>     <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
>     <uses-permission android:maxSdkVersion="32" android:name="android.permission.READ_EXTERNAL_STORAGE" />
>     <uses-permission android:maxSdkVersion="32" android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Run Code Online (Sandbox Code Playgroud)

WRITE_EXTERNAL_STORAGE 的权限请求并不完全相同。媒体捕获附带 SDK 限定符...

camera:  android:name="android.permission.WRITE_EXTERNAL_STORAGE"
capture: android:maxSdkVersion="32" android:name="android.permission.WRITE_EXTERNAL_STORAGE"
Run Code Online (Sandbox Code Playgroud)

手动删除捕获插件条目确实可以使构建成功完成。但是,我不确定结果在功能上是否正确。

问题:这是清单合并代码中的错误,还是有解决方法来避免冲突并保持功能相同?

Nor*_*eau 14

问题:这是清单合并代码中的错误,还是有解决方法来避免冲突并保持功能相同?

这不是一个错误,通常在编写本机项目时,如果两个库尝试使用不同的配置声明相同的权限,则会导致类似的错误。

bug是这样的事实:Cordova 的<edit-config>/<config-file>指令会导致最终结果中的冲突和/或重复指令,这已经是一个 bug 好几年了。

然而,可以使用钩子来纠正项目after_prepare

使用脚本在您的目录中添加stripExtraWriteExternalStoragePerm.js文件:hooks/

const FS = require('fs');
const Path = require('path');

let path = Path.resolve('platforms/android/app/src/main/AndroidManifest.xml');

let manifest = FS.readFileSync(path, {
    encoding: 'utf-8'
});

// Strips ALL occurrences of <uses-permission android:name="androoid.permission.WRITE_EXTERNAL_STORAGE" />
// If you have several conflicts (of different maxSDKVersion, or in different formats) then the regex
// may need to be adjusted, or repeated for each format.
manifest = manifest.replace(/^(\s)+<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" \/>$/gm, '');

FS.writeFileSync(path, manifest);
Run Code Online (Sandbox Code Playgroud)

在您的config.xml添加中:

<widget ...>
    ...
    <platform name="android">
        ...
        <hook type="after_prepare" src="hooks/stripExtraWriteExternalStoragePerm.js" />
    </platform>
</widget>
Run Code Online (Sandbox Code Playgroud)

如果您已经有<platform>Android 块,那么您应该将其添加到现有<platform>块中。

正如 JS 注释所述,插件的任何组合都可能发生冲突,具体取决于它们的声明方式WRITE_EXTERNAL_STORAGE(例如,有或没有maxSDKVersion属性,或指定了不同的maxSDKVersion属性)。如果是这种情况,您可能需要manifest = manifest.replace(...)为您的项目添加额外的行。

我最初为工作编写了钩子脚本,因此代码是在 Apache 许可证下获得 Total Pave Inc. 的版权发布的:https ://gist.github.com/breautek/bd157b8598f9a816f2ec0d45e3d932c8

  • 感谢您提供出色的解决方案。我已经使用 sed 脚本实现了准备后解决方案,但您的方法是首选,并且可能适用于我们过去实现的其他解决方法。 (3认同)