Skip to content

Commit

Permalink
Merge pull request #113 from android/telecom
Browse files Browse the repository at this point in the history
Telecom Alpha -02
  • Loading branch information
lukehopkinsdev authored Oct 13, 2023
2 parents ca46301 + ad3b5e5 commit b18e490
Show file tree
Hide file tree
Showing 22 changed files with 1,852 additions and 7 deletions.
10 changes: 10 additions & 0 deletions .idea/migrations.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ dependencies {
kaptAndroidTest(libs.hilt.compiler)
androidTestImplementation(platform(libs.compose.bom))
androidTestImplementation(libs.androidx.navigation.testing)
androidTestImplementation(libs.compose.ui.test.manifest)
debugImplementation(libs.compose.ui.test.manifest)
androidTestImplementation(libs.compose.ui.test.junit4)
androidTestImplementation(libs.androidx.test.core)
androidTestImplementation(libs.androidx.test.espresso.core)
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/example/platform/app/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,4 @@ class MainApp : Application(), ImageLoaderFactory {
* Entry point for the platform samples catalog using the [CatalogActivity].
*/
@AndroidEntryPoint
class MainActivity : CatalogActivity()
class MainActivity : CatalogActivity()
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class SamplePlugin : Plugin<Project> {
apply("org.jetbrains.kotlin.kapt")
apply("com.google.devtools.ksp")
apply("dagger.hilt.android.plugin")
apply("kotlin-parcelize")
apply<CommonConventionPlugin>()
}

Expand Down Expand Up @@ -113,6 +114,8 @@ class SamplePlugin : Plugin<Project> {
"implementation"(libs.findLibrary("androidx.lifecycle.viewmodel.compose").get())
"implementation"(libs.findLibrary("compose.ui.ui").get())
"implementation"(libs.findLibrary("compose.material3").get())
"implementation"(libs.findLibrary("compose.material.iconsext").get())


"implementation"(libs.findLibrary("coil.compose").get())
"implementation"(libs.findLibrary("coil.video").get())
Expand Down
2 changes: 2 additions & 0 deletions samples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ Send texts and images to other apps using the Android Sharesheet.
Shows the recommended flow to request single runtime permissions
- [Speakable Text](accessibility/src/main/java/com/example/platform/accessibility/SpeakableText.kt):
The sample demonstrates the importance of having proper labels for
- [Telecom Call Sample](connectivity/telecom/src/main/java/com/example/platform/connectivity/telecom/TelecomCallSample.kt):
A sample showcasing how to handle calls with the Jetpack Telecom API
- [TextSpan](user-interface/text/src/main/java/com/example/platform/ui/text/TextSpan.kt):
buildSpannedString is useful for quickly building a rich text.
- [UltraHDR Image Capture](camera/camera2/src/main/java/com/example/platform/camera/imagecapture/Camera2UltraHDRCapture.kt):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ class NotificationSource<T>(
const val ChannelId = "1234"
const val ChannelIntID = 1234

const val ChannelOnGoingIntID = 1233
const val ChannelOnGoingID = "1233"
/*
Notification state sent via in intent to inform receiving broadcast what action the user wants to take
*/
enum class NotificationState(val value: Int) {
Expand All @@ -68,7 +71,7 @@ class NotificationSource<T>(
//Channel for on going call, this has low importance so that the notification is not always shown
@SuppressLint("NewApi")
private val notificationChannelOngoing = NotificationChannel(
ChannelId, "Call Notifications",
ChannelOnGoingID, "Call Notifications",
NotificationManager.IMPORTANCE_LOW,
)

Expand Down Expand Up @@ -140,6 +143,13 @@ class NotificationSource<T>(
notificationManager.notify(ChannelIntID, getIncallNotification())
}

/**
* Will cancel notification and dismiss from sysUI
*/
fun cancelNotification() {
notificationManager.cancel(ChannelIntID)
}

/**
* Creates a notification for incoming calls.
* This notification plays a ringtone and is not dismissible
Expand Down Expand Up @@ -192,7 +202,8 @@ class NotificationSource<T>(
cancelCallIntent, PendingIntent.FLAG_IMMUTABLE,
)

return NotificationCompat.Builder(context, ChannelId)

return NotificationCompat.Builder(context, ChannelOnGoingID)
.setSmallIcon(R.drawable.ic_dialog_dialer)
.setFullScreenIntent(pendingIntent, false)
.setOngoing(true)
Expand Down
19 changes: 19 additions & 0 deletions samples/connectivity/telecom/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Telecom Sample

This module contains the sample for integrating Jetpack Telecom SDK to do audio and/or video calls
using the Android Telecom stack.

The sample simulates a caller app that can make ongoing calls and receive incoming calls. There is
no actual call being made, the sample uses [AudioLoopSource](https://github.com/android/platform-samples/blob/14d464a7c2613e808024f12b2d1c23be15368f4e/samples/connectivity/audio/src/main/java/com/example/platform/connectivity/audio/datasource/AudioLoopSource.kt)
to capture the audio in the device and loop it back to the active endpoint (e.g speaker)

The structure of the sample is the following:

- [TelecomCallSample](src/main/java/com/example/platform/connectivity/telecom/TelecomCallSample.kt):
The entry point of the sample with the options to perform a call or to fake an incoming call
- [TelecomCallActivity](src/main/java/com/example/platform/connectivity/telecom/call/TelecomCallActivity.kt):
The activity to be launch when there is an active call. It handles the UI logic based on the current call.
- [TelecomCallService](src/main/java/com/example/platform/connectivity/telecom/call/TelecomCallService.kt):
A service that manage the logic of the call and communicates with the Telecom SDK
- [TelecomCallRepository](src/main/java/com/example/platform/connectivity/telecom/model/TelecomCallRepository.kt):
The actual logic to communicate with the Telecom SDK
43 changes: 43 additions & 0 deletions samples/connectivity/telecom/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@

/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

@Suppress("DSL_SCOPE_VIOLATION")
plugins {
id("com.example.platform.sample")
alias(libs.plugins.kotlin.android)
}

android {
namespace = "com.example.platform.connectivity.telecom"
}

dependencies {
implementation("androidx.core:core-telecom:1.0.0-alpha02")
implementation(project(mapOf("path" to ":samples:connectivity:audio")))

androidTestImplementation(platform(libs.compose.bom))
androidTestImplementation(libs.androidx.navigation.testing)
androidTestImplementation(libs.compose.ui.test.manifest)
debugImplementation(libs.compose.ui.test.manifest)
androidTestImplementation(libs.compose.ui.test.junit4)
androidTestImplementation(libs.androidx.test.core)
androidTestImplementation(libs.androidx.test.espresso.core)
androidTestImplementation(libs.androidx.test.rules)
androidTestImplementation(libs.androidx.test.runner)
androidTestImplementation(libs.hilt.testing)
androidTestImplementation(libs.junit4)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.platform.connectivity.telecom

import android.Manifest
import android.app.Activity
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.hasContentDescription
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.isOff
import androidx.compose.ui.test.isOn
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.rule.GrantPermissionRule
import org.junit.Before
import org.junit.Rule
import org.junit.Test

/**
* Tests all the samples are present and open.
*
* Note: consider changing the test to use the TestNavController to control navigation instead
*/
class TelecomSampleTest {

/**
* Use the primary activity to initialize the app normally.
*/
@get:Rule(order = 1)
val composeTestRule = createAndroidComposeRule<ComponentActivity>()

private val permissionArray = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
listOf(Manifest.permission.RECORD_AUDIO, Manifest.permission.POST_NOTIFICATIONS)
} else {
listOf(Manifest.permission.RECORD_AUDIO)
}

/**
* Avoids showing permission dialog when running certain samples
*/
@get:Rule(order = 2)
val grantPermissionRule: GrantPermissionRule =
GrantPermissionRule.grant(*permissionArray.toTypedArray())

@Before
fun setUp() {
composeTestRule.setContent {
TelecomCallSample()
}
}

@OptIn(ExperimentalTestApi::class)
@Test
fun testOngoingCall() {
composeTestRule.onNodeWithText("Make fake call").performClick()
composeTestRule.apply {
val isTablet = activity.isDeviceTablet()
Log.d("TelecomSampleTest", "Testing on a tablet? $isTablet")

// Wait till the call is connected
waitUntilExactlyOneExists(hasText("Connected"), 5000)
onNode(hasText("Bob")).assertIsDisplayed()

val onHold = "Pause or resume call"
onNodeWithContentDescription(onHold).apply {
assertIsEnabled()
assert(isOff())
performClick()
}
waitUntilExactlyOneExists(hasContentDescription(onHold) and isOn(), 5000)

// Disconnect call and check
onNodeWithContentDescription("Disconnect call").performClick()
waitUntil {
onAllNodesWithText("Call ended").fetchSemanticsNodes().isNotEmpty()
}
}
}

private fun Activity.isDeviceTablet() =
!packageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_HINGE_ANGLE) &&
resources.configuration.smallestScreenWidthDp >= 600
}
27 changes: 27 additions & 0 deletions samples/connectivity/telecom/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />

<application>
<service
android:name=".call.TelecomCallService"
android:exported="false" />

<activity
android:name=".call.TelecomCallActivity"
android:exported="true"
android:launchMode="singleInstance"
android:showOnLockScreen="true"
android:showWhenLocked="true"
android:turnScreenOn="true" />

<receiver
android:name=".call.TelecomCallBroadcast"
android:exported="false" />
</application>

</manifest>
Loading

0 comments on commit b18e490

Please sign in to comment.