Skip to content

Add opt-in Android prepared consent flow for global recording#26

Open
ananjaemin wants to merge 6 commits into
ChristopherGabba:mainfrom
ananjaemin:main
Open

Add opt-in Android prepared consent flow for global recording#26
ananjaemin wants to merge 6 commits into
ChristopherGabba:mainfrom
ananjaemin:main

Conversation

@ananjaemin

@ananjaemin ananjaemin commented Jul 7, 2026

Copy link
Copy Markdown

Summary

This PR adds an opt-in Android prepared consent flow for global screen recording.

Currently, Android asks for MediaProjection consent when startGlobalRecording() is called. That works for the existing flow, but it makes it difficult for apps that need to request screen recording consent first, move the user through another screen or setup step, and start recording later without showing the system consent dialog again.

This PR introduces a prepared consent lifecycle for that use case while keeping the existing startGlobalRecording() behavior unchanged by default.

What Changed

Android

  • Added requestScreenRecordingConsent() to request MediaProjection consent before recording starts.
  • Added a prepared session state in ScreenRecordingService:
    • idle
    • prepared
    • recording
  • Added releaseScreenRecordingConsent() to release a prepared session when the app no longer needs it.
  • Added isScreenRecordingConsentGranted() and getScreenRecordingSessionState() for checking the prepared/recording state.
  • Added usePreparedConsent to the existing startGlobalRecording().options object.
  • When usePreparedConsent is true, Android starts recording from the prepared MediaProjection session instead of showing the consent dialog again.
  • When usePreparedConsent is missing or false, the existing global recording flow is preserved.

iOS

  • Added compatibility implementations for the new API.
  • These are no-ops because iOS ReplayKit uses the broadcast picker flow and does not expose the same pre-authorization lifecycle.
  • requestScreenRecordingConsent() resolves to true.
  • releaseScreenRecordingConsent() does nothing.
  • isScreenRecordingConsentGranted() returns false.
  • getScreenRecordingSessionState() returns "idle".

JS API

Added:

requestScreenRecordingConsent(): Promise<boolean>;
releaseScreenRecordingConsent(): void;
isScreenRecordingConsentGranted(): boolean;
getScreenRecordingSessionState(): 'idle' | 'prepared' | 'recording';

Extended the existing startGlobalRecording().options object:

startGlobalRecording({
  options: {
    enableMic: true,
    usePreparedConsent: true,
  },
  onRecordingError,
});

usePreparedConsent defaults to false, so existing users do not need to change their code.

Example

  • Added an Android-only Prepared Consent section to the example app.
  • The example allows testing:
    • preparing consent
    • checking session state
    • starting from prepared consent
    • stopping the recording
    • releasing prepared consent

Documentation

  • Added README documentation for the prepared consent APIs.
  • Updated the startGlobalRecording docs to include options.usePreparedConsent.
  • Fixed existing README examples that were using top-level recording options even though the current API already expects recording options under input.options.

Why This Is Opt-In

This is intentionally not enabled automatically.

Existing calls like this keep the current behavior:

startGlobalRecording({
  options: {
    enableMic: true,
  },
  onRecordingError,
});

The prepared flow is only used when the caller explicitly passes:

usePreparedConsent: true

If usePreparedConsent is true but no prepared Android session exists, onRecordingError receives:

{
  name: 'PreparedConsentNotAvailable',
  message: 'Call requestScreenRecordingConsent() before starting with usePreparedConsent.'
}

Example Usage

const prepared = await requestScreenRecordingConsent();

if (prepared) {
  startGlobalRecording({
    options: {
      enableMic: true,
      usePreparedConsent: true,
    },
    onRecordingError: (error) => {
      console.error(error);
    },
  });
}

If the user leaves the flow before recording starts:

releaseScreenRecordingConsent();

Backward Compatibility

  • Existing startGlobalRecording() behavior is unchanged by default.
  • usePreparedConsent is optional and defaults to false.
  • usePreparedConsent is added to the existing options object; this PR does not introduce a new top-level options shape.
  • iOS receives compatibility no-op methods so the shared JS API remains cross-platform safe.
  • The Android prepared session is released after failed prepared starts so a later retry can prepare consent again.

Verification

Verified:

  • yarn typecheck
  • yarn lint
  • git diff --check
  • README code fence / example consistency checks
  • Manual Android device testing for:
    • prepared consent granted
    • prepared recording start
    • stop recording
    • consent denial path

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant