处理网站权限

当网站想要访问用户设备上的某些服务时,它会发送权限请求。本文档将解释如何使用 GeckoView 接收这些请求,并通过授予或拒绝这些权限来响应它们。

权限委托

应用程序通过 PermissionDelegate 与 GeckoView 中的网站权限进行交互。 PermissionDelegate 处理三种主要类型的权限:Android 权限、内容权限和媒体权限。GeckoView 处理的所有网站权限都属于这三类之一。

要接收有关权限请求的通知,您需要实现 PermissionDelegate 接口

private class ExamplePermissionDelegate implements GeckoSession.PermissionDelegate {
    @Override
    public void onAndroidPermissionsRequest(final GeckoSession session,
                                            final String[] permissions,
                                            final Callback callback) { }

    @Override
    public void onContentPermissionRequest(final GeckoSession session,
                                           final String uri,
                                           final int type, final Callback callback) { }

    @Override
    public void onMediaPermissionRequest(final GeckoSession session,
                                         final String uri,
                                         final MediaSource[] video,
                                         final MediaSource[] audio,
                                         final MediaCallback callback) { }
}

然后,您需要将委托注册到您的 GeckoSession 实例。

public class GeckoViewActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ...

        final ExamplePermissionDelegate permission = new ExamplePermissionDelegate();
        session.setPermissionDelegate(permission);

        ...
    }
}

Android 权限

当网站想要访问设备的导航或输入功能时,会请求 Android 权限。

用户通常需要向应用程序授予这些 Android 权限,以及授予内容或媒体网站权限。

当您收到 onAndroidPermissionsRequest 调用时,您还将收到发送请求的 GeckoSession、包含正在请求的权限的数组以及用于响应请求的 Callback。然后,应用程序需要从设备请求这些权限,这可以使用 requestPermissions 完成。

可能的 permissions 值为:ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATIONCAMERARECORD_AUDIO

private class ExamplePermissionDelegate implements GeckoSession.PermissionDelegate {
    private Callback mCallback;

    public void onRequestPermissionsResult(final String[] permissions,
                                           final int[] grantResults) {
        if (mCallback == null) { return; }

        final Callback cb = mCallback;
        mCallback = null;
        for (final int result : grantResults) {
            if (result != PackageManager.PERMISSION_GRANTED) {
                // At least one permission was not granted.
                cb.reject();
                return;
            }
        }
        cb.grant();
    }

    @Override
    public void onAndroidPermissionsRequest(final GeckoSession session,
                                            final String[] permissions,
                                            final Callback callback) {
        mCallback = callback;
        requestPermissions(permissions, androidPermissionRequestCode);
    }
}

public class GeckoViewActivity extends AppCompatActivity {
    @Override
    public void onRequestPermissionsResult(final int requestCode,
                                           final String[] permissions,
                                           final int[] grantResults) {
        if (requestCode == REQUEST_PERMISSIONS ||
            requestCode == REQUEST_WRITE_EXTERNAL_STORAGE) {
            final ExamplePermissionDelegate permission = (ExamplePermissionDelegate)
                    getCurrentSession().getPermissionDelegate();
            permission.onRequestPermissionsResult(permissions, grantResults);
        } else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }
}

内容权限

当网站想要访问设备上存储的内容时,会请求内容权限。可以通过 GeckoView 请求的内容权限包括:地理位置网站通知持久存储XR静音自动播放有声自动播放DRM 媒体访问。此外,跟踪保护例外 被视为一种内容权限。

当您收到 onContentPermissionRequest 调用时,您还将收到发送请求的 GeckoSession,以及存储在 ContentPermission 中的有关正在请求的权限的所有相关信息。然后,应用程序需要向用户展示 UI 以请求权限,并通过返回的 GeckoResult 通知 GeckoView 响应。

以这种方式设置权限后,GeckoView 会在会话之间持久化它,直到它被清除或修改。加载页面时,与其关联的活动权限(已允许和已拒绝)将在 onLocationChange 中作为 ContentPermission 对象列表报告;此外,可以通过调用 getAllPermissions 检查所有存储的内容权限,并通过调用 getPermissions 检查与给定 URI 关联的内容权限。要修改现有权限,您需要关联的 ContentPermission(可以从上述任何方法中检索);然后,使用所需的新的值调用 setPermission,或者如果希望取消设置权限并让网站将来再次请求它,则调用 VALUE_PROMPT

媒体权限

当网站想要访问设备的摄像头和麦克风以播放或录制媒体时,会请求媒体权限。

当您收到 onMediaPermissionRequest 调用时,您还将收到发送请求的 GeckoSession、请求权限的网站的 URI(作为字符串)、可用的视频设备列表(如果请求视频)、可用的音频设备列表(如果请求音频)以及用于响应请求的 MediaCallback

应用程序需要向用户展示 UI 以请求权限,并通过 MediaCallback 通知 GeckoView 响应。

请注意,如果关联的设备权限已被拒绝,但该类别中仍可以访问的视频或音频源存在,则仍会请求媒体权限。消费者有责任确保在这种情况下不显示媒体权限请求。

private class ExamplePermissionDelegate implements GeckoSession.PermissionDelegate {
    @Override
    public void onMediaPermissionRequest(final GeckoSession session,
                                         final String uri,
                                         final MediaSource[] video,
                                         final MediaSource[] audio,
                                         final MediaCallback callback) {
        // Reject permission if Android permission has been previously denied.
        if ((audio != null
                && ContextCompat.checkSelfPermission(GeckoViewActivity.this,
                    Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED)
            || (video != null
                && ContextCompat.checkSelfPermission(GeckoViewActivity.this,
                    Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED)) {
            callback.reject();
            return;
        }

        final String host = Uri.parse(uri).getAuthority();
        final String title;
        if (audio == null) {
            title = getString(R.string.request_video, host);
        } else if (video == null) {
            title = getString(R.string.request_audio, host);
        } else {
            title = getString(R.string.request_media, host);
        }

        // Get the media device name from the `MediaDevice`
        String[] videoNames = normalizeMediaName(video);
        String[] audioNames = normalizeMediaName(audio);

        final AlertDialog.Builder builder = new AlertDialog.Builder(activity);

        // Create drop down boxes to allow users to select which device to grant permission to
        final LinearLayout container = addStandardLayout(builder, title, null);
        final Spinner videoSpinner;
        if (video != null) {
            videoSpinner = addMediaSpinner(builder.getContext(), container, video, videoNames); // create spinner and add to alert UI
        } else {
            videoSpinner = null;
        }

        final Spinner audioSpinner;
        if (audio != null) {
            audioSpinner = addMediaSpinner(builder.getContext(), container, audio, audioNames); // create spinner and add to alert UI
        } else {
            audioSpinner = null;
        }

        builder.setNegativeButton(android.R.string.cancel, null)
               .setPositiveButton(android.R.string.ok,
                                  new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(final DialogInterface dialog, final int which) {
                        // gather selected media devices and grant access
                        final MediaSource video = (videoSpinner != null)
                                ? (MediaSource) videoSpinner.getSelectedItem() : null;
                        final MediaSource audio = (audioSpinner != null)
                                ? (MediaSource) audioSpinner.getSelectedItem() : null;
                        callback.grant(video, audio);
                    }
                });

        final AlertDialog dialog = builder.create();
        dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
                    @Override
                    public void onDismiss(final DialogInterface dialog) {
                        callback.reject();
                    }
                });
        dialog.show();
    }
}

要查看 PermissionsDelegate 的实际应用,您可以在 GeckoView 示例应用程序 中找到完整的示例实现。