How To Record Calls In Android Devices Using Kotlin? - android

I want to make an Android application that can record both incoming and outgoing calls in the background as a service in kotlin and at a particular time in the day, it sends all that recordings to a server by API. I had researched about it all I found is to use Device Policy Manager and Telephoney Manager but it is not much about it on the internet. So can you help me with any article, documentation, or tutorial?

There is no solution from Google as of now. Google has deprecated the feature of recording the calls in it's latest versions of Android OS. Earlier it was possible, I had tried various methods but I was only getting the silent audio when I had tried to record calls. When using Google's Phone application it only allows that application to use the microphone and other things it won't allow any other application to overpower and get that hardware access.
But there are actually two hacks to do that.
Build your own phone application like Truecaller and manage every call and other things from that application by doing this you can get access to managing calls on your device and you will also get the access to record the calls.
If your work is specific to any one mobile example like Samsung, OnePlus, etc. Then you can use any Truecaller or Google's Phone application which will store the recordings of the calls in file storage and then you can make a service to upload that call recording from that particular file location every night at 12 AM or something.

first create MyCallRecordReceiver class
class MyCallRecordReceiver(callRecord: CallRecord) : CallRecordReceiver(callRecord) {
override fun onIncomingCallReceived(context: Context, number: String?, start: Date) {
super.onIncomingCallReceived(context, number, start)
}
}
then in MainActivity
class MainActivity : AppCompatActivity() {
companion object {
private val TAG = MainActivity::class.java.simpleName
}
private lateinit var callRecord: CallRecord
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
callRecord = CallRecord.Builder(this)
.setLogEnable(true)
.setRecordFileName("CallRecorderTestFile")
.setRecordDirName("CallRecorderTest")
.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION)
.setShowSeed(true)
.build()
}
fun StartCallRecordClick(view: View) {
LogUtils.i(TAG, "StartCallRecordClick")
callRecord.startCallReceiver()
}
fun StopCallRecordClick(view: View) {
LogUtils.i(TAG, "StopCallRecordClick")
callRecord.stopCallReceiver()
}
}
In addition Add it as a dependency in your app's build.gradle file
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
and this
compile 'com.github.aykuttasil:CallRecorder:1.5.3'

Related

Capability Info, Health Service, and Ambient Mode

I'm trying to write a fitness companion watch app that would collect heart rate, and calories via HealthServices API, and send them to the device, where we display a workout. I've been following suggested examples:
https://github.com/android/wear-os-samples/tree/main/AlwaysOnKotlin, https://github.com/android/health-samples/tree/2220ea6611770b56350d26502faefc28791f3cbd/health-services/ExerciseSample, and https://github.com/googlecodelabs/ongoing-activity .
I'm trying to achieve the following workflow:
Launch app on Wear device when X happens on the phone
Start exercise client on Wear
Send heart rate/calories update on a regular basis back to phone
Show summary screen, and stop exercise client when Y happens on the phone.
All of these work somewhat well until the watch goes into ambient mode. Then I run into the following problems:
When watch is in ambient mode, the capabilities client on the phone cannot locate watch, and tell it to start exercise. Nor can it tell it to stop exercise. What is a suggested workaround for this?
I use message client on phone to send message to the wearable. But nothing happens here, since the current node is empty.
currentNode?.also { nodeId ->
val sendTask: Task<*>? =
messageClient
?.sendMessage(nodeId, WORKOUT_STATUS_MESSAGE_PATH, "START.toByteArray())
When trying to simulate ambient mode by pressing 'hand' on the watch simulator, the ambient mode listener does not actually trigger to tell me the right thing. The screen gets "stuck" instead of updating to what I want it to.
Code for the ambient mode in MainActivity (I'm still learning Compose, so right now Main activity is where it's at, to eliminate other Compose specific errors):
In Manifest:
<uses-permission android:name="android.permission.WAKE_LOCK" />
In Main Activity:
class MainActivity : FragmentActivity(), AmbientModeSupport.AmbientCallbackProvider {
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ambientController = AmbientModeSupport.attach(this)
setContent {
val ambientEvent by mainViewModel.ambientEventFlow.collectAsState()
StatsScreen(ambientEvent)
}
...
}
override fun getAmbientCallback(): AmbientModeSupport.AmbientCallback = AmbientModeCallback()
inner class AmbientModeCallback : AmbientModeSupport.AmbientCallback() {
override fun onEnterAmbient(ambientDetails: Bundle) {
Timber.e("ambient event: enter: $ambientDetails")
mainViewModel.sendAmbientEvent(AmbientEvent.Enter(ambientDetails))
}
override fun onExitAmbient() {
Timber.e("ambient event: exit")
mainViewModel.sendAmbientEvent(AmbientEvent.Exit)
}
override fun onUpdateAmbient() {
Timber.e("ambient event: update")
mainViewModel.sendAmbientEvent(AmbientEvent.Update)
}
}
I don't see anything printed in this callback, and then consequently, by StateScreen doesn't really do anything when the device enters in the ambient mode.

Android background sound not played on all devices

I'm developing an android app that is mostly run in the background and uses Firebase Realtime Database for data syncing.
I want the app to play a 10 second sound when new data is received from Firebase and I've successfully managed to implement this, however, my solution doesn't work on all devices.
Here's my code:
class MyViewModel constructor(
) : ViewModel() {
private var mediaPlayer: MediaPlayer? = null
fun initRtdListener(context: Context) {
val ref = Firebase.database.getReference("my-path")
mediaPlayer = MediaPlayer.create(context, R.raw.my_short_audio)
ref.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
mediaPlayer?.start()
}
override fun onCancelled(error: DatabaseError) {}
})
}
}
This solution works perfectly on most devices even when the app is running in the background, however, on some devices (like Realme X) the sound is played only when the app is in foreground. What could be the problem? Is it because I play the sound not in the context of a service? If yes, do I really need a service considering that my sounds are short?
P.S. I have two background services which are working without any issues on all devices

Android - display In-app message natively

I would like my app tз display a notification of the incoming messages, but only when the application is active, similar to how many social apps do that.
I.e., the user has my messaging app open and he gets a notification slide in from top, within this Android application.
To my understanding, this is something that is called “in app messages” in Firebase.
However, I wouldn’t like to have firebase as a dependency, as I am not using any part of it: the notifications will be triggered by an open network connection that my app made.
I also so wouldn’t want to involve push notifications as I need this functionality only when the app is active.
What would be the best way to achieve this goal?
Basically what I am asking is how to make my own notification “bubble” in UI that shows up inside my app, similar to how it is done in messaging/dating apps (see Badoo, for example). Mainly I am wondering if there are any implemendations available that I could use or do I have to draw this stuff myself (using Fragments?)
It's a very broad question. So in broad strokes: Use some real time communications technology, such as sockets/websockets to listen for incoming messages, and hook up into lifecycle to start listening when the app moves into foreground (and stop when it moves out) [assuming that is the meaning of app being active - otherwise if you include foreground state, just start listening and don't unlisten) -
class MyListener : LifecycleObserver {
#OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onMoveToForeground() {
listenForNotification()
//start listening
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onMoveToBackground() {
//stop listening
}
where listener would be something like this:
private suspend fun listenForNotification(){
withContext(Dispatchers.IO){
myApi.receive() {
println("this is my notification object: $it")
NotificationHelper.sendNotification($it.message)
}
}
}
And NotificationHelper would be based on Notification Manager to push local notifications (as you wanted them to slide from the top - look like any push notification). Pay close attention to the flags you use to send the notification to make sure it is received and processed by the currently opened activity (do more research on it, separate topic) https://developer.android.com/reference/android/app/NotificationManager
In that same activity use OnNewIntent to receive user's action of tapping on the notification and then do whatever you want to do with it.
Alternatively, do not use local notification but just develop your own UI where you would display these things messaging style. (edit: for example, like this - link. Another one for actually showing notifications without using Notifications lib -link
Or a combination of both local notifications and the above example.
Edit:
*You can also use Firebase messaging to display messages locally.* Yes you would still need a firebase json to init the app, but after that you can construct your messages locally and display them, so it a very lightweight dependency on two libs and aside from initializing you won't need anything else from the firebase server.
Below is an example with two types of messages, card and banner. And of course you can just take the full code on GitHub and extract the part you need and modify it as needed. (the method used here is public for testing the appearance of the message locally - I don't see anything wrong with using it as a vehicle to deliver local notifications, but again the option to take the code and modify is always there)
import android.content.Intent
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.google.firebase.FirebaseApp
import com.google.firebase.inappmessaging.MessagesProto
import com.google.firebase.inappmessaging.display.FirebaseInAppMessagingDisplay
import com.google.firebase.inappmessaging.model.*
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
companion object {
val whiteHex = "#ffffff"
val magHex = "#9C27B0"
val appUrl ="app://open.my.app"
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
val action: String? = intent?.action
val data: Uri? = intent?.data
data?.let {
helloTextView.text ="You just clicked from Firebase Message"
return
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
FirebaseApp.initializeApp(this)
val text = Text.builder()
.setHexColor(whiteHex)
.setText("Local Firebase Message Body")
.build()
val title = Text.builder()
.setHexColor(whiteHex)
.setText("Local Firebase Message Title")
.build()
val imageData = ImageData.builder()
.setImageUrl("https://homepages.cae.wisc.edu/~ece533/images/frymire.png")
.build()
val button = Button.builder()
.setButtonHexColor(whiteHex).setText(text).build()
val campaignMeta = CampaignMetadata("S", "D", true)
val primaryAction = Action.builder()
.setActionUrl(appUrl)
.setButton(button)
.build()
val fmessage = CardMessage.builder()
.setPrimaryAction(primaryAction)
.setBackgroundHexColor(magHex)
.setPortraitImageData(imageData)
.setTitle(title).build(campaignMeta)
val bannerMessage = BannerMessage.builder()
.setAction(primaryAction)
.setImageData(imageData)
.setBackgroundHexColor(magHex)
.setBody(text)
.setTitle(title).build(campaignMeta)
FirebaseInAppMessagingDisplay
.getInstance()
.testMessage(this, bannerMessage, null)
}
}
In build.gradle make sure to add:
implementation 'com.google.firebase:firebase-core:17.2.1'
implementation 'com.google.firebase:firebase-inappmessaging-display:19.0.2'
and intent filter into manifest (to process click on the message)
<data android:scheme="app" android:host="open.my.app" />
also modify launchMode to singleTop to process the click within the same instance of the activity:
<activity android:name=".MainActivity"
android:launchMode="singleTop"
>
and apply
apply plugin: 'com.google.gms.google-services'
The result:
Card message:
Banner message and updating text in response to clicking on the banner:
Added project into GitHub if you are interested - project link. Must add your own google-services.json for firebase (to be able to init the engine only)

Twilio Video Android SDK disconnects after participant lost connection

I've implemented Android client using Twilio SDK to do video calls. It works as expected but I found an edge case which I can not figure out how to fix. Here is essence of video call code:
val connectionOptions = ConnectOptions.Builder(accessToken)
.audioTracks(listOf(audioManager.getLocalAudioTrack()))
.roomName(roomId)
.build()
val roomListener = RoomListener()
Video.connect(androidContext, connectOptions, roomListener)
class RoomEventListener : Room.Listener {
override fun onParticipantDisconnected(room: Room, remoteParticipant: RemoteParticipant) {
// remove participant from the screen, unsubscribe from events
}
override fun onConnectFailure(room: Room, twilioException: TwilioException) {
exitScreenWithErrorMessage(R.string.video_consult_room_connection_error)
}
override fun onReconnected(room: Room) {
_shouldShowReconnectionActivity.value = false
}
override fun onParticipantConnected(room: Room, remoteParticipant: RemoteParticipant) {
onRemoteParticipantConnected(remoteParticipant)
}
override fun onConnected(room: Room) {
_shouldShowConnectionActivity.value = false
this#VideoCallViewModel.room = room
room.remoteParticipants.forEach { onRemoteParticipantConnected(it) }
determineMainParticipant()
onLocalParticipantConnected(room)
}
override fun onDisconnected(room: Room, twilioException: TwilioException?) {
exitVideoConsultScreen()
}
override fun onReconnecting(room: Room, twilioException: TwilioException) {
_shouldShowReconnectionActivity.value = true
}
}
Test case:
Bob joins video call using Android phone
Jane joins same video call from any device (iOS, web, Android)
When Jane loses connection (i.e. turn off internet)
Then Bob sees for 1-2 minutes reconnecting (programmatically "onReconnecting" callback triggered)
[Actual] And Bob disconnected from room (in logs I see Media connection failed or Media activity ceased with error code 53405)
[Expected] Bob stays at the room.
I'm not sure why under such conditions Android client has been disconnected (We tested it on different devices with Android 8/9).
Couple more details:
If Jane exits room using "End call" button (so room.disconnect() code from Twilio SDK has been called) then Bob stays in the room.
When Bob using iOS device (implementation of iOS and Android quite the same) then described use case passes.
We tried 5.0.1 and 5.1.0 version of com.twilio:video-android library.
I noticed Known Issue for Android Twilio Video library in Release notes and I'm not sure can it affects described use case or not:
Unpublishing and republishing a LocalAudioTrack or LocalVideoTrack might not be seen by Participants. As a result, tracks published after a Room.State.RECONNECTED event might not be subscribed to by a RemoteParticipant.
I opened issue on twilio github repo https://github.com/twilio/video-quickstart-android/issues/454 - and this is expected behaviour for twilio video sdk 5.x+. Both for Android and iOS sdks.

How to identify if device has in-display Biometric fingerprint support?

I'm writing a application feature to authenticate user using Biometric fingerprint authentication API. And it worked as expected with combination of BiometricPrompt API.
In general it display own UI dialog so it can be unified across Android device.(Improvement from Fingerprint Manager API)
In one of device testing scenario I come across in-display(on screen, e.g. Oneplus 6T device) fingerprint support instead rear biometric hardware option.
When I run application on it, on call of biometricPrompt.authenticate(..) instead of dialog it display in-display fingerprint authentication option. Which is ok, and manage by internal API of BiometricPrompt.
But it create some inconsistency to manage for developer.
When it provide in-build authentication dialog, all fallback error displayed in dialog itself.
But in case of in-display authentication I didn't found a way where it manage to display error message it-self. And I have to handle this fallback and display in a custom way.
Now question is
Is there a way to manage/display message by in-display authentication view component.
How can identify if device is support in-device biometric authentication.
Edit: Code reference I'm using:
import android.content.Context
import androidx.biometric.BiometricPrompt
import androidx.fragment.app.FragmentActivity
import java.lang.Exception
import java.util.concurrent.Executors
import javax.crypto.Cipher
class BiometricAuthenticationManager(private val context: Context) :
BiometricPrompt.AuthenticationCallback() {
private var biometricPrompt: BiometricPrompt? = null
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
biometricPrompt?.cancelAuthentication()
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
}
fun init(cipher: Cipher, promptInfo: BiometricPrompt.PromptInfo) {
if (context is FragmentActivity) {
val executor = Executors.newSingleThreadExecutor()
biometricPrompt = BiometricPrompt(context, executor, this)
biometricPrompt?.authenticate(promptInfo, BiometricPrompt.CryptoObject(cipher))
} else {
throw Exception(
"Check for FragmentActivity context.")
}
}
}
For further reference about how it look like, please find following attachment.
I try to check same scenario for lock screen, where I guess it uses custom implementation using FingerPrintManager class and display message.
Faced with the same problem some time ago - OnePlus 6T has no Biometric UI, at the same time Samsung A51 - has its own (custom) UI for Fingerprint API and another for BiometricPrompt API.
I tried to watch for Activity window focus loss (For OnePlus - activity do not lose focus, at the same time BiometricPrompt UI leads to focus loss), but appear a problem with the Samsung device.
The only solution that I found, and it works for now:
Need to get the correct device name (for OnePlus for example, it should be not ONEPLUS A6003, but One Plus 6 instead)
Need to perform request to https://m.gsmarena.com/res.php3?sSearch=Device+Name, you should get the list with matches, usually, first one is needed
Parse HTML (from previews step) to get the link for device specification (like https://m.gsmarena.com/oneplus_6t-9350.php)
Now you need to perform a request to this link and again parse HTML and get the "Sensors" section. You should get a string like "Fingerprint (under display, optical), accelerometer, gyro, proximity, compass"
Split this string to get the list with sensors
Now when you need to check if an in-display scanner is present on the device, just check for "fingerprint" and "under display" for the same sensor.
Link with impl. that can be used as an example
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
//Fingerprint API only available on from Android 6.0 (M)
FingerprintManager fingerprintManager = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
if (!fingerprintManager.isHardwareDetected()) {
// Device doesn't support fingerprint authentication
} else if (!fingerprintManager.hasEnrolledFingerprints()) {
// User hasn't enrolled any fingerprints to authenticate with
} else {
// Everything is ready for fingerprint authentication
}
}
Dont Forget to Add
<uses-permission android:name=" android.permission.USE_BIOMETRIC" />

Categories

Resources