Activity Results API returned data is null - android

I am trying out the new Activity Results API by trying to return a parcelable dataesque class from a child activity. Using Alpha4 of the library.
I have setup the Intent with a custom contract 'AddAttendeeContract' as per my understanding of the docs. It compiles and runs and as far as I can see the correct methods are being called but the data is just null.
What might I be missing?
class MainActivity : AppCompatActivity() {
...
override fun onCreate(savedInstanceState: Bundle?) {
... //boilerplate setup nonsense
fab.setOnClickListener {
addAttendee()
}
}
private val addAttendee = registerForActivityResult(AddAttendeeContract()) { attendee: AttendeeData? ->
println("Attendee") // this does not print out
println(attendee) // this does not either
}
}
And the contract
class AddAttendeeContract : ActivityResultContract<Void?, AttendeeData?>() {
override fun createIntent(
context: Context,
input: Void?
): Intent =
Intent(context, AddAttendeeActivity::class.java)
override fun parseResult(
resultCode: Int,
intent: Intent?
): AttendeeData? = when {
resultCode != Activity.RESULT_OK -> null
else -> intent?.getParcelableExtra<AttendeeData>("attendee")
}
}
Finally is the invocation in the child activity class.
class AddAttendeeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
... //boilerplate
add.setOnClickListener { //button on a form
val name: String = view.name.text.toString().trim()
val rate: Double = view.rate.text.toString().trim().toDouble()
val number: Int = view.number.text.toString().trim().toInt()
val intent = Intent(this, MainActivity::class.java).apply {
putExtra("attendee", AttendeeData(name=name, rate=rate, number=number))
}
setResult(Activity.RESULT_OK, intent)
startActivity(intent)
}
}
}
Any insights as to what is going on?

This is solved. The problem was that the second activity was startign a new intent, rather than finishing and returning to the old one.
In the second/child activity had to change the line:
startActivity(intent)
to
finish() and things all worked as expected.

Related

How to avoid to hardcoded cast from context to activity?

I have MainActivity.kt with passing an activity context to MyObj-class:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
MyObj.processing(this)
}
}
MyObj.kt:
class MyObj {
companion object {
fun processing( cx:Context ) {
// -- doesnt work (universal way)
val intent = cx.intent
// -- i have to cast context to activity via hardcoded way (not universal)
val intent = (cx as MainActivity).intent
}
}
}
I would like to have an universal MyObj without a need to cast in a manual way. Is it possible?
Change Context to Activity your function will be like this:
fun processing( ac:Activity ) {
val intent = ac.intent
}
In general, it would be better not to cast anything, but rather pass in the relevant data i.e. Intent in your case:
fun processing (intent: Intent) {
// TODO do your stuff with intent
}

Showing Result returned in a Carousel RecyclerView (Kotlin)

I have a rotating carousel (using carouselrecyclerview) to rotate a list of images around.
The user can call a second activity and then search the images, and then return the selected image’s ID back to the MainActivity.kt.
The MainActivity.kt receives the result (using intent) in the onActivityResult.
I then need to call scrollToPosition (from the carouselLayoutManager) to move the carousel to the position that was selected in the second activity.
As the call to the carouselLayoutManager is within the onCreate, I can’t call it from the onActivityResult? I have tried moving the onActivityResult to within the onCreate, but then the onActivityResult is not called when returning from the second activity.
So, how can I call the code which is within the onCreate from the onActivityResult please?
Any help really appreciated as I’m struggling on this.
MainActivity.kt
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val carouselRecyclerview = findViewById<CarouselRecyclerview>(R.id.recycler)
val list = ArrayList<DataModel>()
// Load the images of the Veg
for (mycount in 0..41) {
list.add(DataModel(VegName[mycount].image, VegName[mycount].name))
}
val adapter = DataAdapter(list)
carouselRecyclerview.adapter = adapter
val carouselLayoutManager = carouselRecyclerview.getCarouselLayoutManager()
carouselLayoutManager.scrollToPosition(1)
carouselRecyclerview.setItemSelectListener(object : CarouselLayoutManager.OnSelected {
override fun onItemSelected(position: Int) {
var ShowIt = findViewById(R.id.textVegName) as TextView
//Cente item
ShowIt.text = list[position].text
}
})
Searchbutton.setOnClickListener {
val intent = Intent(this, SearchActivity::class.java)
startActivityForResult(intent, SEARCH_ACTIVITY_REQUEST_CODE)
}
// Move the carousel to the position received - THIS ISN'T CALLED?
fun setthelocation(SetThisPlace: Int ) {
carouselLayoutManager.scrollToPosition(SetThisPlace)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == SEARCH_ACTIVITY_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
val returnedfrom = VegName.find{ it.name == data?.getStringExtra("result") }
if (returnedfrom==null)
Toast.makeText(applicationContext, "Did not find the result returned!", Toast.LENGTH_LONG).show()
else {
Toast.makeText(applicationContext, "Got = " + returnedfrom.id, Toast.LENGTH_LONG).show()
//Need to call eiter setthelocation() or carouselLayoutManager.scrollToPosition???
return
}
}
}
}
As the call to the carouselLayoutManager is within the onCreate, I can’t call it from the onActivityResult?
I think you're confusing terms. There is no "call to the carouselLayoutManager" - that's a variable to assign to an object, not a function you call.
I have tried moving the onActivityResult to within the onCreate, but then the onActivityResult is not called when returning from the second activity.
onActivityResult is a base-class method that is invoked for you when you use startActivityForResult and close the opened activity. If you "move it to within the onCreate" all you're doing is calling the base class implementation (which does nothing).
So, how can I call the code which is within the onCreate from the onActivityResult please?
The easiest solution would be to hold on to the layout manager as a variable you can use in either onCreate or onActivityResult:
// Class-level property you can use in either function
private lateinit var carouselLayoutManager: LayoutManager
override fun onCreate(...) {
// Replace local val with class-level property
// Instead of this:
// val carouselLayoutManager = carouselRecyclerview.getCarouselLayoutManager()
// Do this: initialize member property to use here and in onActivityResult
carouselLayoutManager = carouselRecyclerview.getCarouselLayoutManager()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == SEARCH_ACTIVITY_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
val returnedfrom = VegName.find{ it.name == data?.getStringExtra("result") }
if (returnedfrom==null)
Toast.makeText(applicationContext, "Did not find the result returned!", Toast.LENGTH_LONG).show()
else {
Toast.makeText(applicationContext, "Got = " + returnedfrom.id, Toast.LENGTH_LONG).show()
//Need to call eiter setthelocation() or carouselLayoutManager.scrollToPosition???
// Now you can call this since it's a member property
carouselLayoutManager.scrollToPosition(...)
return
}
}
}
}
hm, for this I would recommend you to use the NavComponent and send your data through fragments or registering for activity but I don't want to confuse you and I will try to give you a solution for this problem.
I think the easiest way to solve this (in this context) would be to launch the intent as you are doing:
MainActivity.kt ->
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val carouselRecyclerview = findViewById<CarouselRecyclerview>(R.id.recycler)
val list = ArrayList<DataModel>()
// Load the images of the Veg
for (mycount in 0..41) {
list.add(DataModel(VegName[mycount].image, VegName[mycount].name))
}
val adapter = DataAdapter(list)
carouselRecyclerview.adapter = adapter
val carouselLayoutManager = carouselRecyclerview.getCarouselLayoutManager()
carouselLayoutManager.scrollToPosition(1)
carouselRecyclerview.setItemSelectListener(object : CarouselLayoutManager.OnSelected {
override fun onItemSelected(position: Int) {
var ShowIt = findViewById(R.id.textVegName) as TextView
//Cente item
ShowIt.text = list[position].text
}
})
Searchbutton.setOnClickListener {
val intent = Intent(this, SearchActivity::class.java)
startActivity(intent)
}
fun setTheLocation(SetThisPlace: Int ) {
carouselLayoutManager.scrollToPosition(SetThisPlace)
}
// onNewIntent would receive the intent needed to execute your logic.
// I wouldn't use onActivityResult because, IMO, It is dirty code and it is deprecated.
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
if (intent?.hasExtra("position") == true) {
setTheLocation(intent.getIntExtra("position"))
}
}
SearchActivity.kt ->
override fun onCreate(savedInstanceState: Bundle?) {
...
.... your code ...
exampleOfSendingBackFunction()
}
//Here you will send back the position to MainActivity.kt clearing all flags.
fun exampleOfSendingBackFunction() {
val intent = Intent(this, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
putExtras(
"position" to yourPositionVariable
)
}
startActivity(intent)
}
I hope it helps :D

How to get result using registerForActivityResult from within ktor's Routing call running in a non-activity class?

How to get result from another activity (registerForActivity) from with in ktor's Routing API call (eg. /POST) running in a non-activity class?
Background: For an Android app, I run ktor server engine 'netty' in a non-activity class HttpServer.kt. I need to call another app's activity from with in ktor's Routing' POST handler, so I pass 'appCompatActivity' from MainActivity.kt. That's done, just because, I assume, registerForActivityResult() has dependency on UI/life cycle class.
Problem arises when running this as below, as registerForActivityResult() requires to be run earlier (like onCreate() ?), and I don't have such a class in this non-activity class. Moreover, the callback to run when ActivityResult is returned needs to call ktor ApplicationCall's respond which is also a suspend function.
class HttpServer(
private val applicationContext: AppCompatActivity
) {
private val logger = LoggerFactory.getLogger(HttpServer::class.java.simpleName)
private val server = createServer()
private fun ApplicationCall.startSaleActivityForResult() { // <====== *
val activityLauncherCustom =
applicationContext.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
if (result.resultCode == Activity.RESULT_OK || result.resultCode == Activity.RESULT_CANCELED) {
val transactionResultReturned = result.data
// Handle the returned result properly using transactionResultReturned
GlobalScope.launch {
respond(status = HttpStatusCode.OK, TransactionResponse())
}
}
}
val intent = Intent()
// Ignoring statements to create proper action/data intent
activityLauncherCustom.launch(intent) // <====== *
}
fun start() = server.start()
fun stop() = server.stop(0, 0)
private fun createServer(): NettyApplicationEngine {
return GlobalScope.embeddedServer(Netty) {
install(CallLogging)
install(ContentNegotiation) {
gson {
setPrettyPrinting()
}
}
routing {
route("/") {
post {
call.startSaleActivityForResult() // <====== *
}
}
}
}
}
private fun <TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configuration>
CoroutineScope.embeddedServer(
factory: ApplicationEngineFactory<TEngine, TConfiguration>,
module: Application.() -> Unit
): TEngine {
val environment = applicationEngineEnvironment {
this.parentCoroutineContext = coroutineContext + parentCoroutineContext
this.log = logger
this.module(module)
connector {
this.port = 8081
}
}
return embeddedServer(factory, environment)
}
}
Above is what I tried, but gives below error. And I don't have onCreate on this non-activity class.
java.lang.IllegalStateException: LifecycleOwner com.youtap.upti.MainActivity#38dcf06 is attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.
Any suggestions to resolve this problem would be grateful.
Below same above snippet as a screenshot to display helper text on declaration/param types from Android Studio:
And I invoke this server class from onCreate() of MainActivity:
To solve your problem and to hide the complexity you can create an intermediate class for launching activity and waiting for a result to come:
import kotlinx.coroutines.channels.Channel
class Repository(private val activity: MainActivity) {
private val channel = Channel<Int>(1)
suspend fun get(input: String): Int {
activity.activityLauncher.launch(input)
return channel.receive()
}
suspend fun callback(result: Int) {
channel.send(result)
}
}
You can store a reference to a repository and an activity launcher in the MainActivity class:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
CoroutineScope(Dispatchers.IO).launch {
HttpServer(this#MainActivity).also { it.start() }
}
}
val activityLauncher = registerForActivityResult(MySecondActivityContract()) { result ->
GlobalScope.launch {
repository.callback(result!!)
}
}
val repository = Repository(this)
}
My second activity and a contract looks like the following:
class ChildActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_child)
val result = Intent()
result.putExtra("name", 6666)
result.data = Uri.parse("http://mydata")
setResult(Activity.RESULT_OK, result)
finish()
}
}
class MySecondActivityContract : ActivityResultContract<String, Int?>() {
override fun createIntent(context: Context, input: String?): Intent {
return Intent(context, ChildActivity::class.java)
.putExtra("my_input_key", input)
}
override fun parseResult(resultCode: Int, intent: Intent?): Int? = when {
resultCode != Activity.RESULT_OK -> null
else -> intent?.getIntExtra("name", 42)
}
override fun getSynchronousResult(context: Context, input: String?): SynchronousResult<Int?>? {
return if (input.isNullOrEmpty()) SynchronousResult(42) else null
}
}
The most simplest part is routing handler:
routing {
route("/") {
post {
val result = (applicationContext as MainActivity).repository.get("input")
call.respondText { result.toString() }
}
}
}
This solution works but only one request is processed at the same time and it's not robust because Activity may be destroyed before HTTP server or repository objects.

Passing an integer between Activities Doesn’t return the correct value?

I’m using Kotlin and trying to return a number from two Activities.
So from MainActivity I get a click (Searchbutton), this starts up my second activity: SearchActivity. When a click happens it then (should!) returns the number 59 to the MainActivity, which I should be able to see in Log.d.
What I actually see is I get the default value “0” being returned to MainActivity?
I’m assuming that the default value indicates that the value hasn’t been passed through the two activities?
Any help really appreciated!
MainActivity.java
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Searchbutton.setOnClickListener {
val intent = Intent(this, SearchActivity::class.java)
startActivity(intent)
var gotthisInt = intent.getIntExtra("MY_KEY", 1);
Log.d("TAG", "What was received= " + gotthisInt)
}
}
}
SearchActivity.java
class SearchActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.fragment_container)
Searchbutton2.setOnClickListener {
val intent = Intent(this#SearchActivity, MainActivity::class.java).apply {
putExtra("MY_KEY", 59)
}
startActivity(intent)
}
}
}
In Android to pass data between Activity, you should use startActivityForResult(Intent, int). So change your code to.
MainActivity.java
class MainActivity : AppCompatActivity() {
private val SEARCH_ACTIVITY_REQUEST_CODE = 1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Searchbutton.setOnClickListener {
val intent = Intent(this, SearchActivity::class.java)
startActivityForResult(intent, SEARCH_ACTIVITY_REQUEST_CODE)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == SEARCH_ACTIVITY_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
val gotthisInt = data?.getIntExtra("MY_KEY", 1) ?: 1
Log.d("TAG", "What was received = $gotthisInt")
}
}
}
}
SearchActivity.java
class SearchActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.fragment_container)
Searchbutton2.setOnClickListener {
val data = Intent().apply { putExtra("MY_KEY", 59) }
setResult(Activity.RESULT_OK, data)
finish()
}
}
}

How do you pass data from current activity to Previous activity

I am trying to pass a value from Activity 3 to Activity 2 but I am getting null Value. If I click Back button its going to previous Activity but value is null. Added the suggested Approach code below. but still not able to get the results.
Suggested Approach:
Activity :3
override fun onBackPressed() {
sendDataBackToPreviousActivity()
super.onBackPressed()
}
private fun sendDataBackToPreviousActivity()
{
val navBarTitle21=intent.getStringExtra(TestProjectMenuViewHolder.TEST_TITLE_NAME)
val intent=Intent().apply { putExtra("ReturnMessage",navBarTitle21)}
setResult(Activity.RESULT_OK,intent)
}
Activity:2
Main Class:
companion object {
const val START_ACTIVITY_3_REQUEST_CODE = 0
}
val intent=Intent(this,TestProjectMenuDetail::class.java)
startActivityForResult(intent, START_ACTIVITY_3_REQUEST_CODE)
Declared outside Main Class:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == START_ACTIVITY_3_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
val message = data!!.getStringExtra("ReturnMessage")
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
println("Message Value: $message")
}
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
First Approach:
Activity:3
val navBarTitle= intent.getStringExtra(TestMenuViewHolder.TEST_TITLE_KEY)
supportActionBar?.title=navBarTitle//Something Like "StackOverFlow". THis is for back Button
TestMenuDetail:
val navBarTitle2=intent.getStringExtra(TestMenuViewHolder.TEST_TITLE_NAME)
val TestVar=Intent(this#TestMenuDetail,TestMenuList::class.java)
intent.putExtra("TestVar2",navBarTitle2)
println("Test Value $navBarTitle2")//Test Value Hello
Activity:2
TestMenuList:
val navBarTitle3=intent.getStringExtra("TestVar2")
println("Helllo Test: $navBarTitle3")//Helllo Test: null
You should use startActivityForResult API to achieve your task.
Activity2.kt
class Activity2 : AppCompatActivity() {
companion object {
const val START_ACTIVITY_3_REQUEST_CODE = 0
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity2)
// Start Activity3
val intent = Intent(this, Activity3::class.java)
startActivityForResult(intent, START_ACTIVITY_3_REQUEST_CODE)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == START_ACTIVITY_3_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
val message = data!!.getStringExtra("message")
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
}
Activity3.kt
class Activity3 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity3)
}
override fun onBackPressed() {
sendDataBackToPreviousActivity()
super.onBackPressed()
}
/**
* Send data back to previous activity which start this one, you can call this method when users press on back key
* or when users press on a view (button, image, etc) on this activity.
*/
private fun sendDataBackToPreviousActivity() {
val intent = Intent().apply {
putExtra("message", "This is a message from Activity3")
// Put your data here if you want.
}
setResult(Activity.RESULT_OK, intent)
}
}
Ok here is what I would do:
Override the onBackPressed method and then pass the variable value inside the method with an Intent. And in activity 2 receive the value from activity 3.
In activity 3
#override
public void onBackPressed (){
Intent intent = new Intent(getApplicationContext(), Activity2.class);
intent.put("value_key", value);
startActivity(intent);
}
Receive value in activity 2
getIntent.getValue("value_key");
Don't forget to check the syntax, I just wrote it from my phone. Hope it helps!
You can always use SharedPreferences, and then clear them, after receiving data in previous activity. It's 100% effective way. Put it:
val sharedPreference = getSharedPreferences("prefs name",Context.MODE_PRIVATE)
var editor = sharedPreference.edit()
editor.putString("your value name","value")
editor.commit()
and get it:
sharedPreference.getString("your value name","default value")
but of course you have to open preferences again in previous activity ;)
val sharedPreference = getSharedPreferences("prefs name",Context.MODE_PRIVATE)

Categories

Resources