In this tutorial, we will be building an Android video calling app using the Telecom framework, VideoSDK, and Firebase. The app will allow users to make video calls with other users who have installed the app on their devices. We will also implement a notification system that notifies users when they receive incoming calls.
Here's what we'll cover in this tutorial:
1. Setting up the Android project and adding required dependencies
2. Implementing the IncomingCallScreen to handle incoming video calls
3. Implementing the OutgoingCallScreen to initiate video calls
4. Implementing the MeetingActivity to manage ongoing video calls
5. Integrating Firebase Cloud Messaging for push notifications
6. Testing the app on multiple devices
Before we begin, make sure you have Android Studio installed on your system and create a new project with an Empty Activity template.
1. Setting up the Android project and adding required dependencies
First, let's add the necessary dependencies to our build.gradle files:
In `app/build.gradle`, add the following dependencies:
```groovy
dependencies {
implementation 'com.google.android.gms:play-services-auth:19.0.0'
implementation 'com.google.firebase:firebase-messaging:22.0.0'
implementation 'com.videosdk.cloud:reactnative-plugin:4.3.0'
}
```
In `project/build.gradle`, add the following dependencies under `allprojects -> repositories` section:
```groovy
repositories {
google()
mavenCentral()
jcenter()
maven { url 'https://jitpack.io' }
}
```
Now, let's create a new class named `IncomingCallScreen` that will handle incoming video calls:
```java
public class IncomingCallScreen extends AppCompatActivity {
private static final String TAG = "IncomingCallScreen";
private TextView tvName;
private ImageButton ibAccept, ibReject;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_incoming_call_screen);
initUI();
// Get the caller's name from intent extras
String callerName = getIntent().getStringExtra("name");
if (TextUtils.isEmpty(callerName)) {
callerName = "Unknown";
}
tvName.setText(callerName);
}
private void initUI() {
tvName = findViewById(R.id.tvName);
ibAccept = findViewById(R.id.ibAccept);
ibReject = findViewById(R.id.ibReject);
ibAccept.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Accept the incoming call and start a new MeetingActivity
Intent intent = new Intent(IncomingCallScreen.this, MeetingActivity.class);
intent.putExtra("name", tvName.getText().toString());
startActivity(intent);
finish();
}
});
ibReject.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Reject the incoming call and end it on both sides
TelecomManager telecomManager = (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
if (telecomManager != null) {
telecomManager.endCall();
}
finish();
}
});
}
}
```
Next, let's create a new class named `OutgoingCallScreen` that will handle outgoing video calls:
```java
public class OutgoingCallScreen extends AppCompatActivity {
private static final String TAG = "OutgoingCallScreen";
private TextView tvName;
private ImageButton ibHangUp;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_outgoing_call_screen);
initUI();
// Get the callee's name from intent extras
String calleeName = getIntent().getStringExtra("name");
if (TextUtils.isEmpty(calleeName)) {
calleeName = "Unknown";
}
tvName.setText(calleeName);
}
private void initUI() {
tvName = findViewById(R.id.tvName);
ibHangUp = findViewById(R.id.ibHangUp);
ibHangUp.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// End the ongoing call and return to MainActivity
TelecomManager telecomManager = (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
if (telecomManager != null) {
telecomManager.endCall();
}
finish();
}
});
}
}
```
Now, let's create a new class named `MeetingActivity` that will manage ongoing video calls:
```java
public class MeetingActivity extends AppCompatActivity {
private static final String TAG = "MeetingActivity";
private TextView tvName;
private ImageButton ibHangUp, ibWebcam, ibMic;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_meeting);
initUI();
// Get the meeting ID and participant name from intent extras
String meetingId = getIntent().getStringExtra("id");
String participantName = getIntent().getStringExtra("name");
// Initialize VideoSDK with your App's credentials
VideoSDK.init(this, "YOUR_APP_ID", "YOUR_API_KEY", "YOUR_API_SECRET");
// Start a new video meeting with the given parameters
VideoSDK.startMeeting(meetingId, participantName, true, true, null, null);
}
private void initUI() {
tvName = findViewById(R.id.tvName);
ibHangUp = findViewById(R.id.ibHangUp);
ibWebcam = findViewById(R.id.ibWebcam);
ibMic = findViewById(R.id.ibMic);
ibHangUp.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// End the ongoing video meeting and return to MainActivity
VideoSDK.leaveMeeting();
finish();
}
});
ibWebcam.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Toggle the local participant's webcam visibility
VideoSDK.toggleWebcam();
}
});
ibMic.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Toggle the local participant's microphone mute/unmute state
VideoSDK.toggleMic();
}
});
}
}
```
Finally, let's create a new class named `MainActivity` that will handle incoming and outgoing video calls:
```java
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialize Firebase Cloud Messaging and register for push notifications
FirebaseMessaging.getInstance().subscribeToTopic("all");
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (TelecomManager.ACTION_INCOMING_CALL.equals(intent.getAction())) {
// Handle incoming video call by starting IncomingCallScreen activity
Intent i = new Intent(this, IncomingCallScreen.class);
i.putExtra("name", intent.getStringExtra(TelecomManager.EXTRA_Incoming_CALL_PERFORMER_NAME));
startActivity(i);
} else if (intent.hasExtra("id") && intent.hasExtra("name")) {
// Handle outgoing video call by starting OutgoingCallScreen activity
Intent i = new Intent(this, OutgoingCallScreen.class);
i.putExtra("name", intent.getStringExtra("name"));
startActivity(i);
}
}
}
```
Now that we have implemented the necessary classes and XML layout files, let's move on to integrating Firebase Cloud Messaging for push notifications.
2. Integrating Firebase Cloud Messaging for push notifications
First, create a new file named `new_intent_action.xml` inside the `res/xml` directory:
```xml
<?xml version="1.0" encoding="utf-8"?>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
```
Next, update the `AndroidManifest.xml` file by adding the following permissions and services:
```xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera2" />
<uses-feature android:name="android.hardware.camera" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".IncomingCallScreen" />
<activity android:name=".OutgoingCallScreen" />
<activity android:name=".MeetingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
</activity>
<!-- Add the following service declaration -->
<service android:name=".FirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
```
Now, create a new class named `FirebaseMessagingService` that will handle incoming push notifications:
```java
public class FirebaseMessagingService extends com.google.firebase.messaging.FirebaseMessagingService {
private static final String TAG = "FirebaseMsgService";
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
// Handle data payload when app is in foreground or background
if (remoteMessage.getData().size() > 0) {
Log.d(TAG, "Message data payload: " + remoteMessage.getData());
String callerId = remoteMessage.getData().get("id");
String callerName = remoteMessage.getData().get("name");
// Create an intent to start the IncomingCallScreen activity with the given parameters
Intent i = new Intent(this, MainActivity.class);
i.putExtra("id", callerId);
i.putExtra("name", callerName);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, i,
PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE);
String channelId = getString(R.string.default_notification_channel_id);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.ic_stat_ic_notification)
.setContentTitle("Incoming Call")
.setContentText(callerName)
.setPriority(NotificationCompat.PRIORITY_HIGH)
// Set the intent that will fire when the user taps the notification
.setContentIntent(pendingIntent)
.setAutoCancel(true);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Since android Oreo notification channel is needed.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(channelId,
"Call Channel",
NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(channel);
}
notificationManager.notify(0 /* ID of notification */, builder.build());
}
}
}
```
Now that we have implemented the necessary classes and XML layout files, let's move on to testing the app on multiple devices.
3. Testing the app on multiple devices
To test the video calling feature between two devices, follow these steps:
1. Install the APK file generated by Android Studio on both devices.
2. Open the app on one device and click the "Call" button to initiate a call with another user's name (e.g., "John Doe").
3. The other device should receive a push notification indicating an incoming video call from the first device.
4. Tap the notification to accept the incoming call, which will open the IncomingCallScreen activity on the second device.
5. Click the "Accept" button to start a new MeetingActivity with both users participating in the video call.
6. To end the call, click the "Hang Up" button on either device.
That's it! You have now successfully built an Android video calling app using the Telecom framework, VideoSDK, and Firebase Cloud Messaging. For additional features like chat messaging and screen sharing, feel free to refer to our documentation. If you encounter any issues with the implementation, don't hesitate to reach out to us through our Discord community.