Skip to content

fix(react-native-callkeep): add missing permissions for CallKeep inte… #285

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

sultanmyrza
Copy link

Motivation

Recently, I wanted to add a feature to show the native call UI for iOS and Android. This was challenging due to missing documentation. I am not the only one facing this issue—see this comment. I have submitted a PR to improve the react-native-callkeep (expo) docs.

Typically, react-native-callkeep (with @config-plugins/react-native-callkeep) is used together with react-native-webrtc (and @config-plugins/react-native-webrtc).
Both config plugins add some permissions, but if you only use react-native-callkeep (with @config-plugins/react-native-callkeep), certain permissions are missing.

For example, when using only react-native-callkeep, your app.json might look like this:

{
  "expo": {
    "newArchEnabled": false,
    "android": {
      "permissions": [
        // These are added automatically by "@config-plugins/react-native-callkeep"
        // See: https://github.com/expo/config-plugins/blob/566f54e785147d88cb4c98490c3e536ee7ef3001/packages/react-native-callkeep/src/withCallkeep.ts#L118
        "android.permission.BIND_TELECOM_CONNECTION_SERVICE",
        "android.permission.FOREGROUND_SERVICE",
        "android.permission.READ_PHONE_STATE",
        "android.permission.CALL_PHONE",

        // However, you still need these permissions:
        // See: https://github.com/sultanmyrza/react-native-callkeep/blob/aadf30d2250b622c8e054757ba1d626a2df79fd6/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java#L115
        "android.permission.READ_PHONE_NUMBERS",
        "android.permission.RECORD_AUDIO"
      ],
    },
    "plugins": [
      "@config-plugins/react-native-callkeep"
    ],
  }
}

Solution

If you look at RNCallKeepModule.java, you will see that the following permissions are required in the Java code:

private static String[] permissions = {
    Build.VERSION.SDK_INT < 30 ? Manifest.permission.READ_PHONE_STATE : Manifest.permission.READ_PHONE_NUMBERS,
    Manifest.permission.CALL_PHONE,
    Manifest.permission.RECORD_AUDIO
};

Test Plan

Manual Test Steps:

  1. Create a fresh Expo project (development build).
  2. Run: npx expo install react-native-callkeep @config-plugins/react-native-callkeep
  3. Run the development build on a physical Android device.

Sample code for basic testing:

app/utils/callkeep.ts
import CallKeep, { IOptions } from "react-native-callkeep";

const options: IOptions = {
  ios: {
    appName: "NoonExperiments",
  },
  android: {
    alertTitle: "Permission required",
    alertDescription: "This app needs access to your phone calling features",
    cancelButton: "Cancel",
    okButton: "OK",
    additionalPermissions: [],
  },
};

export async function setupCallKeep() {
  const result = await CallKeep.setup(options);
  console.log(`ℹ️ CallKeep setup result: ${result}`);
  CallKeep.setAvailable(true);
}

export function displayIncomingCall(uuid: string, handle: string, callerName: string) {
  console.log("📞 Display incoming call:", uuid, handle, callerName);
  CallKeep.displayIncomingCall(uuid, handle, callerName);
}

// Setup immediately when file is imported
setupCallKeep()
  .then(() => console.log("✅ CallKeep initialized"))
  .catch((err) => console.error("❌ CallKeep setup failed:", err));
app/_layout.tsx
import '@/utils/callkeep'; // Import early to initialize CallKeep as soon as possible

// ...existing code...
app/(tabs)/index.tsx
import { Image } from "expo-image";
import { Button, StyleSheet } from "react-native";

import { HelloWave } from "@/components/HelloWave";
import ParallaxScrollView from "@/components/ParallaxScrollView";
import { ThemedText } from "@/components/ThemedText";
import { ThemedView } from "@/components/ThemedView";
import { displayIncomingCall } from "@/utils/callkeep";

export default function HomeScreen() {
  return (
    <ParallaxScrollView
      headerBackgroundColor={{ light: "#A1CEDC", dark: "#1D3D47" }}
      headerImage={
        <Image
          source={require("@/assets/images/partial-react-logo.png")}
          style={styles.reactLogo}
        />
      }
    >
      <ThemedView style={styles.titleContainer}>
        <ThemedText type="title">Welcome!</ThemedText>
        <HelloWave />
      </ThemedView>
      <ThemedView style={styles.stepContainer}>
        <ThemedText type="subtitle">Step 1: Try it</ThemedText>
        <Button
          title="Simulate Incoming Call"
          onPress={() =>
            displayIncomingCall("9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d", "1234567890", "Test Caller")
          }
        />
        <Button
          title="Simulate Incoming Call With 3 seconds delay"
          onPress={() => {
            setTimeout(() => {
              displayIncomingCall(
                "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
                "1234567890",
                "Test Caller"
              );
            }, 3000);
          }}
        />
      </ThemedView>
    </ParallaxScrollView>
  );
}

const styles = StyleSheet.create({
  titleContainer: {
    flexDirection: "row",
    alignItems: "center",
    gap: 8,
  },
  stepContainer: {
    gap: 8,
    marginBottom: 8,
  },
  reactLogo: {
    height: 178,
    width: 290,
    bottom: 0,
    left: 0,
    position: "absolute",
  },
});

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