How to verify sendBroadcast been send with specific intent? - android

In test I want to verify that sendBroadcast been send. Here is the code from view layer requireContext().sendBroadcast(myYntent, myPermission)
And here I am launching the fragment in test
#Test
fun broadcastIntent_test() = runBlocking {
val intent : Intent = Intent()
reactions.emit(WidgetFragmentReaction.BroadcastIntent(intent))
launchFragmentInHiltContainer<WidgetFragment> {
// val context = InstrumentationRegistry.getInstrumentation().targetContext
// val shadow = shadowOf(context)
// val shadowedIntent: Intent = shadow.peekNextStartedActivity()
// maybe some of these might help
}
}

Related

registerForActivityResult is not working consistently

I implemented a registerForActivityResult in fragment to launch another activity and to get data from the activity. The problem is that the registerForActivityResult sometimes works and sometimes it doesn't. I put a log to be printed and I found that when it works the log is not printed. That means it doesn't go into the function.
val getContent = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ result:ActivityResult ->
Log.i("register", "out")
if(result.resultCode == Activity.RESULT_OK)
{
val data = result.data
val note = data?.getParcelableExtra<Note>("note")
Log.i("register", "in")
adapter.mNotes[notePos].title = note?.title.toString()
adapter.mNotes[notePos].notes = note?.notes.toString()
adapter.notifyItemChanged(notePos)
}
}
private fun initRecycleView()
{
adapter = NotesAdapter(notesList){note, pos ->
val intent = Intent(this.context, NotingActivity::class.java).apply {
putExtra("note",note)
notePos = pos
}
getContent.launch(intent)
}
rvItems.adapter = adapter
rvItems.layoutManager = GridLayoutManager(this.context, 2)
(rvItems.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
}
override fun onBackPressed() {
intent.putExtra("note", note)
setResult(Activity.RESULT_OK, intent)
finish()
super.onBackPressed()
}

How do I store a String for later use in Kotlin?

I am trying to store the String from the Hours and Number for use in a new activity. I have found ways of doing it with intent but do not want the strings sent through to the next activity. Is there any way of me saving my String data and being able to call it to a different activity?
class ChecksStartUp : AppCompatActivity() {
lateinit var hours: EditText
lateinit var number: EditText
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
getSupportActionBar()?.hide()
setContentView(R.layout.activity_Checks_pg1)
val number = findViewById(R.id.machineFleetNumberId)
val hours = findViewById(R.id.machineHoursId)
val dButton = findViewById<RadioButton>(R.id.dButtonId)
val eButton = findViewById<RadioButton>(R.id.eButtonId)
val hButton = findViewById<RadioButton>(R.id.hButtonId)
val proceedButton = findViewById<Button>(R.id.proceedButtonId)
proceedButton.setOnClickListener {
if (dButton.isChecked()) {
val intent = Intent(this, DPage::class.java)
startActivity(intent)
}
if (eButton.isChecked()) {
val intent = Intent(this, EPage::class.java)
startActivity(intent)
}
if (hButton.isChecked()) {
val intent = Intent(this, HPage::class.java)
}
}
}
}
Alot of different ways to store data. Simplest way is to create singleton class - object.
In Android studio create new Kotlin file/class, choose Object.
this is short example:
object GlobalVariables { var hourses:String?="" var number:Int?= null }
access:
GlobalVariables.number=3 - set
val number =GlobalVariables.number - get
You can access this data everywhere you want in your classes, fragments and activites , but remember - when app is terminated data is immediately lost.

requestActivityTransitionUpdates never calls the registered BroadcastReceiver

I am coding a simple app that measures all available sensors of the android device (Wifi, BT, etc). One of them is the user activity (via ActivityRecognition API), but I can't make it works properly.
I code a class to do everything related to user activity. I want to get only 4 states and one attribute to store the current one:
var VEHICLE = "vehicle"
var WALKING = "walking"
var STILL = "still"
var UNKNOWN = "unknown"
private var current: String? = null
It also includes a BroadcastReceiver object to handle activity transitions:
private var recognitionHandler = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (ActivityRecognitionResult.hasResult(intent)) {
val result = ActivityRecognitionResult.extractResult(intent)
val activity = result.mostProbableActivity
current = when(activity.type) {
DetectedActivity.IN_VEHICLE,
DetectedActivity.ON_BICYCLE -> VEHICLE
DetectedActivity.WALKING,
DetectedActivity.RUNNING -> WALKING
DetectedActivity.STILL -> STILL
else -> UNKNOWN
}
}
}
}
The class also have two methods to define the intent and request:
private fun createIntent() : PendingIntent {
val intent = Intent(context, recognitionHandler.javaClass)
val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0)
context.registerReceiver(recognitionHandler, IntentFilter())
return pendingIntent
}
private fun createRequest() : ActivityTransitionRequest {
val types = listOf(
DetectedActivity.IN_VEHICLE,
DetectedActivity.WALKING,
DetectedActivity.RUNNING,
DetectedActivity.ON_BICYCLE,
DetectedActivity.STILL
)
val transitions = mutableListOf<ActivityTransition>()
types.forEach { activity ->
transitions.add(
ActivityTransition.Builder()
.setActivityType(activity)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
.build()
)
}
return ActivityTransitionRequest(transitions)
}
And also one to start listening:
override fun start(onResult: (res: String?) -> Unit) {
// ...
intent = createIntent()
val request = createRequest()
ActivityRecognition.getClient(context)
.requestActivityTransitionUpdates(request, intent)
.addOnSuccessListener {
Log.d("UserActivity Service info", "listening...")
}
.addOnFailureListener { e ->
Log.d("UserActivity Service error", e.toString())
}
// ...
}
The problem is that the current attribute is always null. I think I have some issues with intent or handler registration, but I have no idea where.
Does someone have any comments? :)
Thanks!
This is your problem. In this code from createIntent():
val intent = Intent(context, recognitionHandler.javaClass)
val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0)
context.registerReceiver(recognitionHandler, IntentFilter())
return pendingIntent
You return a PendingIntent that you use in the call to requestActivityTransitionUpdates(). However, that PendingIntent refers to a dynamically created inner class (your BroadcastReceiver) and Android cannot instantiate that class.
You also additionally call registerReceiver(), however you pass an empty IntentFilter in that call so the registered BroadcastReceiver is never called.
To fix the problem, you can either provide a correctIntentFilter that matches your PendingIntent OR you can refactor your BroadcastReceiver into a proper class (not a private inner class) and make sure that you've added the BroadcastReceiver to your manifest and make it publicly available (exported="true").
Here's an example of how to do this using a BroadcastReceiver:
https://steemit.com/utopian-io/#betheleyo/implementing-android-s-new-activity-recognition-transition-api

Android ConnectionService incoming calls

I'm trying to implement iOS callkit behavior on Android. I'm receiving a push notification from firebase and I want to show "incoming call" screen to the user. To do it I use ConnectionService from android.telecom package and other classes.
Here is my call manager class:
class CallManager(context: Context) {
val telecomManager: TelecomManager
var phoneAccountHandle:PhoneAccountHandle
var context:Context
val number = "3924823202"
init {
telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
this.context = context
val componentName = ComponentName(this.context, CallConnectionService::class.java)
phoneAccountHandle = PhoneAccountHandle(componentName, "Admin")
val phoneAccount = PhoneAccount.builder(phoneAccountHandle, "Admin").setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED).build()
telecomManager.registerPhoneAccount(phoneAccount)
val intent = Intent()
intent.component = ComponentName("com.android.server.telecom", "com.android.server.telecom.settings.EnableAccountPreferenceActivity")
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
}
#TargetApi(Build.VERSION_CODES.M)
fun startOutgoingCall() {
val extras = Bundle()
extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true)
val manager = context.getSystemService(TELECOM_SERVICE) as TelecomManager
val phoneAccountHandle = PhoneAccountHandle(ComponentName(context.packageName, CallConnectionService::class.java!!.getName()), "estosConnectionServiceId")
val test = Bundle()
test.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle)
test.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, VideoProfile.STATE_BIDIRECTIONAL)
test.putParcelable(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras)
try {
manager.placeCall(Uri.parse("tel:$number"), test)
} catch (e:SecurityException){
e.printStackTrace()
}
}
#TargetApi(Build.VERSION_CODES.M)
fun startIncomingCall(){
if (this.context.checkSelfPermission(Manifest.permission.MANAGE_OWN_CALLS) == PackageManager.PERMISSION_GRANTED) {
val extras = Bundle()
val uri = Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null)
extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, uri)
extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle)
extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true)
val isCallPermitted = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
telecomManager.isIncomingCallPermitted(phoneAccountHandle)
} else {
true
}
Log.i("CallManager", "is incoming call permited = $isCallPermitted")
telecomManager.addNewIncomingCall(phoneAccountHandle, extras)
}
}
}
And my custom ConnectionService implementation:
class CallConnectionService : ConnectionService() {
override fun onCreateOutgoingConnection(connectionManagerPhoneAccount: PhoneAccountHandle?, request: ConnectionRequest?): Connection {
Log.i("CallConnectionService", "onCreateOutgoingConnection")
val conn = CallConnection(applicationContext)
conn.setAddress(request!!.address, PRESENTATION_ALLOWED)
conn.setInitializing()
conn.videoProvider = MyVideoProvider()
conn.setActive()
return conn
}
override fun onCreateOutgoingConnectionFailed(connectionManagerPhoneAccount: PhoneAccountHandle?, request: ConnectionRequest?) {
super.onCreateOutgoingConnectionFailed(connectionManagerPhoneAccount, request)
Log.i("CallConnectionService", "create outgoing call failed")
}
override fun onCreateIncomingConnection(connectionManagerPhoneAccount: PhoneAccountHandle?, request: ConnectionRequest?): Connection {
Log.i("CallConnectionService", "onCreateIncomingConnection")
val conn = CallConnection(applicationContext)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
conn.connectionProperties = Connection.PROPERTY_SELF_MANAGED
}
conn.setCallerDisplayName("test call", TelecomManager.PRESENTATION_ALLOWED)
conn.setAddress(request!!.address, PRESENTATION_ALLOWED)
conn.setInitializing()
conn.videoProvider = MyVideoProvider()
conn.setActive()
return conn
}
override fun onCreateIncomingConnectionFailed(connectionManagerPhoneAccount: PhoneAccountHandle?, request: ConnectionRequest?) {
super.onCreateIncomingConnectionFailed(connectionManagerPhoneAccount, request)
Log.i("CallConnectionService", "create outgoing call failed ")
}
}
And my Connection implementation is like that:
class CallConnection(ctx:Context) : Connection() {
var ctx:Context = ctx
val TAG = "CallConnection"
override fun onShowIncomingCallUi() {
// super.onShowIncomingCallUi()
Log.i(TAG, "onShowIncomingCallUi")
val intent = Intent(Intent.ACTION_MAIN, null)
intent.flags = Intent.FLAG_ACTIVITY_NO_USER_ACTION or Intent.FLAG_ACTIVITY_NEW_TASK
intent.setClass(ctx, IncomingCallActivity::class.java!!)
val pendingIntent = PendingIntent.getActivity(ctx, 1, intent, 0)
val builder = Notification.Builder(ctx)
builder.setOngoing(true)
builder.setPriority(Notification.PRIORITY_HIGH)
// Set notification content intent to take user to fullscreen UI if user taps on the
// notification body.
builder.setContentIntent(pendingIntent)
// Set full screen intent to trigger display of the fullscreen UI when the notification
// manager deems it appropriate.
builder.setFullScreenIntent(pendingIntent, true)
// Setup notification content.
builder.setSmallIcon(R.mipmap.ic_launcher)
builder.setContentTitle("Your notification title")
builder.setContentText("Your notification content.")
// Use builder.addAction(..) to add buttons to answer or reject the call.
val notificationManager = ctx.getSystemService(
NotificationManager::class.java)
notificationManager.notify("Call Notification", 37, builder.build())
}
override fun onCallAudioStateChanged(state: CallAudioState?) {
Log.i(TAG, "onCallAudioStateChanged")
}
override fun onAnswer() {
Log.i(TAG, "onAnswer")
}
override fun onDisconnect() {
Log.i(TAG, "onDisconnect")
}
override fun onHold() {
Log.i(TAG, "onHold")
}
override fun onUnhold() {
Log.i(TAG, "onUnhold")
}
override fun onReject() {
Log.i(TAG, "onReject")
}
}
According to the document to show user incoming calcustomon UI - I should do some actions in onShowIncomingCallUi() method. But it just does not called by the system.
How can I fix it?
I was able to get it to work using a test app and Android Pie running on a Pixel 2 XL.
From my testing the important parts are to ensure:
That Connection.PROPERTY_SELF_MANAGED is set on the connection. Requires a minimum of API 26.
You have to register your phone account.
You have to set PhoneAccount.CAPABILITY_SELF_MANAGED in your capabilities when registering the phone account. That is the only capability that I set. Setting other capabilities caused it to throw an exception.
Finally, you need to ensure that you have this permission set in AndroidManifest.xml. android.permission.MANAGE_OWN_CALLS
So, I would check your manifest to ensure you have the permissions and also ensure the capabilities are set correctly. It looks like everything else was set correctly in your code above.
Hope that helps!

Get string extra from activity Kotlin

I want to get a string extra in another activity from an intent. This is the way to create my intent
val intent = Intent(this, Main2Activity::class.java)
intent.putExtra("samplename", "abd")
startActivity(intent)
How can i get the value of this intent in the another activity
Answer found, in the next activity, you have to do this to get the string:
val ss:String = intent.getStringExtra("samplename").toString()
LOAD
val value: String = txt_act_main.text.toString() // or just your string
val intent = Intent(this, SecondActivity::class.java)
intent.putExtra("value", value)
startActivity(intent)
//option 2 all inner classes should be implemented to Serializable
getIntent().putExtra("complexObject", clickedTitle);
GET
var bundle :Bundle ?=intent.extras
var message = bundle!!.getString("value") // 1
var strUser: String = intent.getStringExtra("value") // 2
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
//option 2
var myProg = intent.getSerializableExtra("complexObject") as MenuModel
IMPLICIT (To Share with other apps)
val value: String = txt_act_main.text.toString()
var intent = Intent()
intent.action = Intent.ACTION_SEND
intent.putExtra(Intent.EXTRA_TEXT, value)
intent.type="text/plain"
startActivity(Intent.createChooser(intent,"Share to "))
Can use this code :
val bundle = intent.extras
var sampleName: String = ""
if (bundle != null) {
sampleName = bundle.getString("samplename")
}
you can check if the intent value is null or not
val bundle :Bundle ?=intent.extras
if (bundle!=null){
val message = bundle.getString("object") // 1
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
In Main2Activity you can have your code like this :
val intent = getIntent();
val myValue = intent.getStringExtra("key")
Log.d(TAG,"myValue"+myValue)
The accepted answer doesn't solve the case where the intent is not there. Because when the key is not exist in intent, getStringExtra() will give you null even its signature indicates a String rather than a String?.
You can use val text:String = intent.getStringExtra(intentKey) ?: "" to make sure no NPE happened.
But one more answer here:
This is for the case where, you try to retrieve the string from intent, if the value is there, we get the value, otherwise, it will go back to the previous screen because this intent is critical. Something wrong will happen but we don't want to crash the activity.
private fun getStringFromIntentOrShowError(intentKey: String):String {
val text:String? = intent.getStringExtra(intentKey)
if (text == null) {
showDialog(
"Error",
"No $intentKey found"
) {
it.dismiss()
finish()
}
return ""
}
return text
}
// I use anko to show a dialog, you can use your one.
private fun showDialog(
title:String,
message:String,
yesButtonCallback: (d:DialogInterface) -> Unit
) {
alert(message, title){ yesButton{
yesButtonCallback(it)
} }.show()
}
Then you can use it like this:
val text:String = getStringFromIntentOrShowError("asd")
and text will always have a value
You can use this simple Kotlin Extension
fun Intent.getData(key: String): String {
return extras?.getString(key) ?: "intent is null"
}
this Extension checks if the intent value is null, if the value is null it will return intent is null otherwise, it will return the value
use it like this
val username = intent.getData("username")
First you need to initialize the intent variable.
Then take out the data like we do in java :)
val intent: Intent = intent
var data = intent.getStringExtra("mydata")

Categories

Resources