Deep Links navigation in Jetpack Compose - android

I want to use deep links with Jetpack Compose's Nav Host and followed this page on Compose Navigation: https://developer.android.com/jetpack/compose/navigation#deeplinks
My implementation:
AndroidManifest.xml:
<application ...>
<activity
...
android:allowTaskReparenting="true"
android:launchMode="singleInstance">
...
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="xkcd.com" />
<data android:scheme="https" android:host="www.xkcd.com" />
</intent-filter>
</activity>
</application>
MainActivity.onCreate().setContent{}
val rootUri = "https://www.xkcd.com"
NavHost(navController = navController, startDestination = "mainView") {
composable("mainView", deepLinks = listOf(navDeepLink { uriPattern = rootUri })) {
MainContent()
}
composable(
route = "singleView/{number}",
arguments = listOf(navArgument("number") { type = NavType.IntType }),
deepLinks = listOf(navDeepLink { uriPattern = "$rootUri/{number}" })
) { backStackEntry ->
val number = backStackEntry.arguments?.getInt("number")
SingleView(number)
}
}
If I now click on a corresponding link the app opens but the navigation doesn't work

The problem is with the Activity launchMode:
The documentation says that is strongly recommended to always use the default launchMode of standard when using Navigation. When using standard launch mode, Navigation automatically handles deep links by calling handleDeepLink() to process any explicit or implicit deep links within the Intent. However, this does not happen automatically if the Activity is re-used when using an alternate launchMode such as singleTop. In this case, it is necessary to manually call handleDeepLink() in onNewIntent(), as shown in the following example:
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
navController.handleDeepLink(intent)
}

I got it working by removing
android:launchMode="singleInstance"
from the manifest

To Kamil, you could save navController to viewModel, then reuse it inside onNewIntent method.
class HomeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val model: MainModel by viewModels()
setContent {
val navController = rememberNavController()
model.navController = navController
MyTheme {
Scaffold {
NavigationComponent(navController)
}
}
}
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
val model: MainModel by viewModels()
model.navController.handleDeepLink(intent)
}
}

you also can do this
setContent {
DisposableEffect(Unit) {
val listener = Consumer<Intent> {
//do som
}
addOnNewIntentListener(listener)
onDispose { removeOnNewIntentListener(listener) }
}
}

Related

Android Media3 Session & Controller - Playback not starting

I'm trying to implement the Android Media3 MediaSessionService and MediaController but for some reason the playback doesn't start. What am I doing wrong? I think I did everything exactly as described in Play media in the background.
PlaybackService.kt
class PlaybackService : MediaSessionService() {
private var mediaSession: MediaSession? = null
override fun onCreate() {
super.onCreate()
val player = ExoPlayer.Builder(this).build()
mediaSession = MediaSession.Builder(this, player).build()
}
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? =
mediaSession
override fun onDestroy() {
mediaSession?.run {
player.release()
release()
mediaSession = null
}
super.onDestroy()
}
}
MainActivity.kt
class MainActivity : ComponentActivity() {
private lateinit var controllerFuture: ListenableFuture<MediaController>
private lateinit var controller: MediaController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
log("onCreate MainActivity")
setContent {
TestMediaTheme {
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
Button(onClick = {
//val url = "android.resource://$packageName/${R.raw.test}"
val url = "https://download.samplelib.com/mp3/sample-15s.mp3"
play(url)
}) {
Text(text = "Play")
}
}
}
}
}
override fun onStart() {
super.onStart()
val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java))
controllerFuture = MediaController.Builder(this, sessionToken).buildAsync()
controllerFuture.addListener(
{
controller = controllerFuture.get()
initController()
},
MoreExecutors.directExecutor()
)
}
override fun onStop() {
MediaController.releaseFuture(controllerFuture)
super.onStop()
}
private fun initController() {
//controller.playWhenReady = true
controller.addListener(object : Player.Listener {
override fun onMediaMetadataChanged(mediaMetadata: MediaMetadata) {
super.onMediaMetadataChanged(mediaMetadata)
log("onMediaMetadataChanged=$mediaMetadata")
}
override fun onIsPlayingChanged(isPlaying: Boolean) {
super.onIsPlayingChanged(isPlaying)
log("onIsPlayingChanged=$isPlaying")
}
override fun onPlaybackStateChanged(playbackState: Int) {
super.onPlaybackStateChanged(playbackState)
log("onPlaybackStateChanged=${getStateName(playbackState)}")
}
override fun onPlayerError(error: PlaybackException) {
super.onPlayerError(error)
log("onPlayerError=${error.stackTraceToString()}")
}
override fun onPlayerErrorChanged(error: PlaybackException?) {
super.onPlayerErrorChanged(error)
log("onPlayerErrorChanged=${error?.stackTraceToString()}")
}
})
log("start=${getStateName(controller.playbackState)}")
log("COMMAND_PREPARE=${controller.isCommandAvailable(COMMAND_PREPARE)}")
log("COMMAND_SET_MEDIA_ITEM=${controller.isCommandAvailable(COMMAND_SET_MEDIA_ITEM)}")
log("COMMAND_PLAY_PAUSE=${controller.isCommandAvailable(COMMAND_PLAY_PAUSE)}")
}
private fun play(url: String) {
log("play($url)")
log("before=${getStateName(controller.playbackState)}")
controller.setMediaItem(MediaItem.fromUri(url))
controller.prepare()
controller.play()
log("after=${getStateName(controller.playbackState)}")
}
private fun getStateName(i: Int): String? {
return when (i) {
1 -> "STATE_IDLE"
2 -> "STATE_BUFFERING"
3 -> "STATE_READY"
4 -> "STATE_ENDED"
else -> null
}
}
private fun log(message: String) {
Log.e("=====[TestMedia]=====", message)
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.INTERNET" />
<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/Theme.TestMedia"
tools:targetApi="33">
<activity
android:name=".MainActivity"
android:exported="true"
android:theme="#style/Theme.TestMedia">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<service
android:name=".PlaybackService"
android:exported="true"
android:foregroundServiceType="mediaPlayback">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService" />
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
</application>
</manifest>
And here's the debug log:
01:51:22.004 E onCreate MainActivity
01:51:22.544 E start=STATE_IDLE
01:51:22.544 E COMMAND_PREPARE=true
01:51:22.544 E COMMAND_SET_MEDIA_ITEM=true
01:51:22.544 E COMMAND_PLAY_PAUSE=true
//click 1
01:51:24.027 E play(https://download.samplelib.com/mp3/sample-15s.mp3)
01:51:24.027 E before=STATE_IDLE
01:51:24.029 E onPlaybackStateChanged=STATE_BUFFERING
01:51:24.029 E after=STATE_BUFFERING
01:51:24.053 E onPlaybackStateChanged=STATE_ENDED
//click 2
01:51:25.715 E play(https://download.samplelib.com/mp3/sample-15s.mp3)
01:51:25.715 E before=STATE_ENDED
01:51:25.716 E onPlaybackStateChanged=STATE_BUFFERING
01:51:25.716 E after=STATE_BUFFERING
//click 3
01:51:26.749 E play(https://download.samplelib.com/mp3/sample-15s.mp3)
01:51:26.749 E before=STATE_BUFFERING
01:51:26.750 E after=STATE_BUFFERING
//click 4
01:51:30.172 E play(https://download.samplelib.com/mp3/sample-15s.mp3)
01:51:30.172 E before=STATE_BUFFERING
01:51:30.173 E after=STATE_BUFFERING
So it looks as if after the first click the player buffers and then immediately ends, and after the second click it just buffers indefinitely. Anyone got an idea what might be the problem?
Finally I found the solution thanks to this issue and this question. It seems like the Media3 Guide is missing a very crucial part.
From the onAddMediaItems documentation: Note that the requested media items don't have a MediaItem.LocalConfiguration (for example, a URI) and need to be updated to make them playable by the underlying Player.
In the end I solved it by overriding MediaSession.Callback.onAddMediaItems
class PlaybackService : MediaSessionService(), MediaSession.Callback {
private var mediaSession: MediaSession? = null
override fun onCreate() {
super.onCreate()
val player = ExoPlayer.Builder(this).build()
mediaSession = MediaSession.Builder(this, player).setCallback(this).build()
}
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? =
mediaSession
override fun onDestroy() {
mediaSession?.run {
player.release()
release()
mediaSession = null
}
super.onDestroy()
}
override fun onAddMediaItems(
mediaSession: MediaSession,
controller: MediaSession.ControllerInfo,
mediaItems: MutableList<MediaItem>
): ListenableFuture<MutableList<MediaItem>> {
val updatedMediaItems = mediaItems.map { it.buildUpon().setUri(it.mediaId).build() }.toMutableList()
return Futures.immediateFuture(updatedMediaItems)
}
}
and then replacing
controller.setMediaItem(MediaItem.fromUri(url))
by
val media = MediaItem.Builder().setMediaId(url).build()
controller.setMediaItem(media)

automatically starting my app using kotlin android broadcastreceiver

I am working on an android application using kotlin, I want this app to automatically start when the phone is done booting, or whenever the user reboots the phone. I had made some research and realized it can be implemented using broadcast, however I have tried some codes but it's not working. Please 🙏🙏 someone should help, I will be submitting the app in about 4days and this feature is keep me back.
BootUpReceiver.kt
package com.example.jofi
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
class BootUpReceiver:BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (Intent.ACTION_BOOT_COMPLETED == intent!!.action) {
val i = Intent(context, MainActivity::class.java)
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context!!.startActivity(i)
}
}
}
MainActivity.kt
class MainActivity : AppCompatActivity(), TextToSpeech.OnInitListener {
lateinit var reciever:StartUpOnBootUpReciever
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// val intent1 = Intent(this#MainActivity, RunServiceOnBoot::class.java)
// startService(intent1)
reciever = StartUpOnBootUpReciever()
reciever = StartUpOnBootUpReciever()
IntentFilter(Intent.ACTION_BOOT_COMPLETED).also {
registerReceiver(reciever, it)
}
}
}
ManiFest.xml
I had included the following in my manifest
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<receiver android:name=".StartUpOnBootUpReciever"
android:exported="true"
android:enabled="true"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED"
tools:node="merge">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>

NFC does not work properly with active KIOSK mode

I am new in creating the KIOSK app with NFC and I faced a problem. The app works in KIOSK mode and scans NFC cards. But sometimes NFC stops working, and I reboot the device to keep NFC working, but sometimes even after a reboot NFC doesn't work. The device OS is Android 7.
This is the Manifest file:
<activity
android:screenOrientation="portrait"
android:name=".MainActivity"
android:launchMode="singleTask"
android:finishOnTaskLaunch="true"
android:clearTaskOnLaunch="true"
android:stateNotNeeded="true"
android:excludeFromRecents="true"
android:autoRemoveFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
And this is Code from activity class:
class MainActivity : BaseActivity() {
private val kioskManager by lazy {
KioskManager(
activity = this,
activityName = MainActivity::class.java.name,
componentName = AppDeviceAdminReceiver.getComponentName(this)
)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
isAdmin = devicePolicyManager.isDeviceOwnerApp(packageName)
if (intent.getStringExtra(KioskManager.LOCK_ACTIVITY_KEY) == KioskManager.UNLOCK) {
kioskManager.stopLock()
} else {
kioskManager.startLock()
}
setContentView(R.layout.activity_main)
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
val tagFromIntent: Tag? = intent?.getParcelableExtra(NfcAdapter.EXTRA_TAG)
// handle NFC data
}
override fun onResume() {
super.onResume()
enableNfcForegroundDispatch()
}
override fun onPause() {
disableNfcForegroundDispatch()
super.onPause()
}
private fun enableNfcForegroundDispatch() {
try {
val intent = Intent(this, javaClass).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
val nfcPendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
nfcAdapter.enableForegroundDispatch(this, nfcPendingIntent, null, null)
} catch (ex: IllegalStateException) {
ex.printStackTraceDebug()
}
}
private fun disableNfcForegroundDispatch() {
try {
nfcAdapter.disableForegroundDispatch(this)
} catch (ex: IllegalStateException) {
ex.printStackTraceDebug()
}
}
}
I spend a month to fix this bug, but can't find an answer?
How to fix this issue?
Thanks!

Start Activity with Quick Tile, Android Studio

I currently have a MainActivity (QR Code reader) and a QuickTile, I'm trying to link both (if I click on the tile, it starts the QR Code reader).
The MainActivity is classic:
class MainActivity : AppCompatActivity() {
private lateinit var codeScanner: CodeScanner
override fun onCreate(savedInstanceState: Bundle?) {
// ALL THE CODE FOR THE QR CODE
}
}
and the AndroidManifest with the service:
<service
android:name=".MyTileService"
android:enabled="true"
android:exported="true"
android:label="QR"
android:icon="#drawable/ic_baseline_border_clear_24"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
<action
android:name="android.service.quicksettings.action.QS_TILE"/>
</intent-filter>
</service>
Now the TileService, I don't know what to do on the onClick(), last thing I tried was to open a second activity:
class MyTileService : TileService() {
override fun onTileAdded() {
super.onTileAdded()
// Update state
qsTile.state = Tile.STATE_INACTIVE
// Update looks
qsTile.updateTile()
}
override fun onClick() {
super.onClick()
val myIntent = Intent(this, MainActivity::class.java)
startActivityAndCollapse(myIntent)
}
}

When a call arrives, the onCallAdded(Call call) method of InCallService is not called while main thread is still running

What solution I am looking for
How to make onCallAdded(Call call) method of InCallService called every time when I make call in the main thread ( using #repeat for cycling).
Background
I'm writing a small Android auto test APP to automatically make calls then do web browsing...etc for 10 cycles.
My code is based on this article : Answer incoming call using android.telecom and InCallService
How it is working
Here is the MainActivity : Start to make call to $autoCallNumber repeatedly When I click button(autoCall).
import android.Manifest
import android.content.Intent
import android.os.Bundle
import android.os.CountDownTimer
import android.telecom.TelecomManager
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.PermissionChecker
import androidx.core.net.toUri
import kotlinx.android.synthetic.main.activity_call.*
import kotlinx.android.synthetic.main.activity_dialer.*
import kotlinx.android.synthetic.main.activity_main.*
import java.util.concurrent.TimeUnit
class MainActivity : AppCompatActivity() {
private val TAG = "${javaClass.simpleName} Wynne"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun onStart() {
super.onStart()
offerReplacingDefaultDialer()
autoCall.setOnClickListener {
repeat(2) { i ->
makeCall()
Log.d(TAG,"We are on the ${i + 1}. loop")
TimeUnit.SECONDS.sleep(5);
}
}
}
private fun makeCall() {
if (PermissionChecker.checkSelfPermission(this, Manifest.permission.CALL_PHONE) == PermissionChecker.PERMISSION_GRANTED) {
var User: GeneralSettings = applicationContext as GeneralSettings
User.ongoingCalltype = "MO"
val uri = "tel:${User.autoCallNumber}".toUri()
startActivity(Intent(Intent.ACTION_CALL, uri))
} else {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.CALL_PHONE),
DialerActivity.REQUEST_PERMISSION
)
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == DialerActivity.REQUEST_PERMISSION && PermissionChecker.PERMISSION_GRANTED in grantResults) {
makeCall()
}
}
private fun offerReplacingDefaultDialer() {
if (getSystemService(TelecomManager::class.java).defaultDialerPackage != packageName) {
Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER)
.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, packageName)
.let(::startActivity)
}
}
}
Here is the Object : Ongoingcall (for me to handle answer or hangup)
package com.github.arekolek.phone
import android.telecom.Call
import android.telecom.VideoProfile
import android.util.Log
import io.reactivex.subjects.BehaviorSubject
import timber.log.Timber
object OngoingCall {
val state: BehaviorSubject<Int> = BehaviorSubject.create()
private val TAG = "${javaClass.simpleName} Wynne"
private val callback = object : Call.Callback() {
override fun onStateChanged(call: Call, newState: Int) {
Log.d(TAG,"${call.toString()}")
state.onNext(newState)
Log.d(TAG,"New state : $newState")
}
}
var call: Call? = null
set(value) {
field?.unregisterCallback(callback)
value?.let {
it.registerCallback(callback)
state.onNext(it.state)
}
field = value
}
fun answer() {
call!!.answer(VideoProfile.STATE_AUDIO_ONLY)
}
fun hangup() {
call!!.disconnect()
}
}
Here is the CallService (Rewrite onCallAdded/ onCallRemoved of InCallService to handling activities when call arrives/ remove)
package com.github.arekolek.phone
import android.telecom.Call
import android.telecom.InCallService
import android.util.Log
class CallService : InCallService() {
private val TAG = "${javaClass.simpleName} Wynne"
override fun onCallAdded(call: Call) {
OngoingCall.call = call
try {
CallActivity.start(this, call)
Log.d(TAG,"Start Call Activity")
}
catch ( e : Exception ) {
Log.d(TAG,"$e")
}
}
override fun onCallRemoved(call: Call) {
OngoingCall.call = null
}
}
Here is Call Activity (update call info on UI and hangup unknown call...etc)
package com.github.arekolek.phone
import android.R.id.button1
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.telecom.Call
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.addTo
import kotlinx.android.synthetic.main.activity_call.*
import java.util.concurrent.TimeUnit
class CallActivity : AppCompatActivity() {
private val TAG = javaClass.simpleName
private val disposables = CompositeDisposable()
private lateinit var ongoingCallNumber: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_call)
ongoingCallNumber = intent.data.schemeSpecificPart
}
override fun onStart() {
super.onStart()
var User: GeneralSettings = applicationContext as GeneralSettings
OngoingCall.state
.subscribe(::updateUi)
.addTo(disposables)
OngoingCall.state
.filter { it == Call.STATE_DISCONNECTED }
.delay(1, TimeUnit.SECONDS)
.firstElement()
.subscribe { finish() }
.addTo(disposables)
if (User.ongoingCalltype == "MO") {
Handler().postDelayed(Runnable { hangup.performClick() }, 5000)
Log.d(TAG, "Wynne : End MO call after 5 second")
User.ongoingCalltype = ""
} else if (ongoingCallNumber == User.autoAnswerNumber) {
Handler().post(Runnable { answer.performClick() })
Log.d(TAG, "Wynne : Answer incoming call ${User.autoAnswerNumber} ")
}
else{
Handler().post(Runnable { hangup.performClick() })
Log.d(TAG, "Wynne : End Unknown incoming call $ongoingCallNumber ")
}
}
#SuppressLint("SetTextI18n")
private fun updateUi(state: Int) {
callInfo.text = "${state.asString().toLowerCase().capitalize()}\n$ongoingCallNumber"
answer.isVisible = state == Call.STATE_RINGING
hangup.isVisible = state in listOf(
Call.STATE_DIALING,
Call.STATE_RINGING,
Call.STATE_ACTIVE
)
}
override fun onStop() {
super.onStop()
disposables.clear()
}
companion object {
fun start(context: Context, call: Call) {
Intent(context, CallActivity::class.java)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.setData(call.details.handle)
.let(context::startActivity)
}
}
}
Here is the AndroidManifest (Bind .CallService (re-written InCallService))
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.arekolek.phone"
>
<uses-permission android:name="android.permission.CALL_PHONE" />
<application
android:name=".GeneralSettings"
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"
android:windowSoftInputMode="stateAlwaysVisible|adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<!-- Handle links from other applications -->
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.DIAL" />
<!-- Populate the system chooser -->
<category android:name="android.intent.category.DEFAULT" />
<!-- Handle links in browsers -->
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="tel" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.DIAL" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<service
android:name=".CallService"
android:permission="android.permission.BIND_INCALL_SERVICE">
<meta-data
android:name="android.telecom.IN_CALL_SERVICE_UI"
android:value="true"
/>
<intent-filter>
<action android:name="android.telecom.InCallService" />
</intent-filter>
</service>
<activity
android:name=".CallActivity">
</activity>
</application>
</manifest>
Here is GeneralSettings (Global Variables)
package com.github.arekolek.phone
import android.app.Application
class GeneralSettings : Application() {
val autoCallNumber: String = "0988102544"
var ongoingCalltype: String = ""
val autoAnswerNumber: String = "0905112980"
}
What is the problem then
onCallAdded() of CallService supposed to be called after makeCall() each cycle in #repeat.
However, based on the debug message, I assume that
the onCallAdded() of CallService is not called until autoCall.setOnClickListener completed.
Here is the Debug message
16:52:24.859 D/MainActivity Wynne: We are on the 1. loop
16:52:29.895 D/MainActivity Wynne: We are on the 2. loop
16:52:35.041 D/CallService Wynne: Start Call Activity
16:52:35.048 D/CallService Wynne: Start Call Activity
16:52:35.116 D/CallActivity: Wynne : End MO call after 5 second
16:52:40.161 D/OngoingCall Wynne: New state : 10
16:52:40.419 D/OngoingCall Wynne: New state : 7
16:52:41.551 D/CallActivity: Wynne : End Unknown incoming call 0988102544
Can somebody tell me:
How to make onCallAdded() of CallService called every time when I run makeCall() in the main thread? so that I can disconnect the call before the next cycle.
Thank you for taking the time to read my question.
I think if you are declaring IN_CALL_SERVICE_UI=true in metadata you need to set your application as the default dialer as well.
<meta-data
android:name="android.telecom.IN_CALL_SERVICE_UI"
android:value="true"
/>
You can do it with the following ADB command:
adb shell telecom set-default-dialer $your_package_name
The quote from docs is below:
METADATA_IN_CALL_SERVICE_UI
A boolean meta-data value indicating
whether an InCallService implements an in-call user interface. Dialer
implementations (see getDefaultDialerPackage()) which would also like
to replace the in-call interface should set this meta-data to true in
the manifest registration of their InCallService.
Android Developers website

Categories

Resources