I'm currently trying to develop a simple app and for my use case is easier to use Composable function for the menu, and then move to an activity for other operations.
Right now I have two classes:
MainActivity: a ComponentActivity that runs some composable functions
NewActivity: an Activity that runs its own class code and has a xml for the design
I successfully managed to move from MainActivity to NewActivity with:
val context = LocalContext.current
val intent = Intent(context, NewActivity::class.java)
startActivity(context, intentntent, null)
Once I reach the NewActivity, I can't however go back to the composable class. In particular, calling back:
val Intent : Intent = Intent(this,MainActivity::class.java)
startActivity(intent)
makes the application stuck.
Is there a way to switch back to the composable class? If not, what alternative do you suggest?
Thanks in advance
Related
Suppose I have a notification, that when clicked, launches my app's activity. It's a notification about a message, in a conversation, and so it launches the activity passing the conversationId as an argument. When the activity is launched by that intent from the notification, it should open MessagesScreen, which is a deeply nested screen in the app, passing to it conversationId.
What is the best way to do this in Compose? In the good old Fragments or Activities you just navigated straight to it, but with Compose is a little trickier. The path to the MessagesScreen is as follows:
SplashScreen (checks for authentication) -> HomeScreen (if authenticated) -> ConversationScreen -> MessagesScreen
I can't just navigate straight to MessagesScreen by having the compose's NavController be stored in the Activity, since I need to go through SplashScreen to check for authentication. Also, I don't know the Compose's implication of navigating to a deeply nested component from the Activity's onCreate().
What I currently do is have a field in my global ViewModel called notificationConversationId, that is set on my Activity's onCreate if it was passed by the notification's intent:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val conversationId = intent.getStringExtra("conversation_id") ?: ""
globalViewModel.notificationConversationId = conversationId
// ...
Then, in a LaunchedEffect in my HomeScreen I observe this field, and if it is not empty, I navigate to the MessagesScreen, and set it to an empty string, so the LaunchedEffect is not executed again.
val conversationId = globalViewModel.notificationConversationid
LaunchedEffect(conversationId) {
if (conversationId.isNotEmpty()) {
val path = getMessagespath(conversationId = conversationId)
globalViewModel.notificationConversationId = ""
navController.navigate(path)
}
}
It works, but it is horrendous. Is there a better way to accomplish this in Compose? Thanks in advance.
I got a question. I have two activities in my app. In first one, when I click the button, I need something to happen in the second one. How can I do it? If that button would be in the second activity I would just do it by:
button.setOnClickListener {}
But how can I do it when button is in the other activity? It's worth adding that code, that tells what should happen, must be in that second activity, just like it was in that "setOnClickListener". Sorry, I'm starting with Android development.
You could communicate between two activities via broadcast or intent.
But it make logic more complex.
So I suggest use two fragments instead two activities.
If you use two fragment in one activity, you can easy communicate between two fragments.
You can look more detailed information about fragment from this URL.
https://developer.android.com/guide/fragments
To achieve the intended flow you may try the below approach,
Start activity 2 on button click from activity 1.
On activity 2 place your code in onCreate so, once activity 2 loads up your code will fire up.
Activity 1:
button.setOnClickListener{
val intent = Intent(this, second::class.java)
startActivity(intent)
}
Activity 2:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
CallDefinedFunctionHere();
}
You cannot be sure that both activities are present at the same moment since the system might destroy inactive one therefore you cannot trigger any code from activity A inside activity B.
What you can do you can start activity B with an intent and some parameters describing what should happen inside activity B.
or
You can communicate by writing something down to a persistent storage (like SharedPreferences) and then when the other activity is resumed (active again) reading it, reacting to it and then removing it from the storage (to make sure you do not handle it twice).
You can pass data in the intent that opens the second activity.
// In first activity:
buttonX.setOnClickListner {
val intent = Intent(MySecondActivity::class.java).apply {
putExtra("wasFromButtonX", true)
}
startActivity(intent)
}
// In second activity:
fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val wasLaunchedFromButtonX = intent.getBooleanExtra("wasFromButtonX", false)
// Above line uses false as default, so it will only be true if you explicitly
// put the extra in the Intent that started this Activity.
if (wasLaunchedFromButtonX) {
// do alternate setup here
}
}
How to pass information between Activities is explained in the introductory documentation here.
Create a function in class where 2nd activity are defined like this.
public void refresh(){}
Now Call that in your 1st activity where you want to call 1st after any action.
button.setOnClickListener {((MainActivity) Objects.requireNonNull(getActivity())).refresh();}
I am getting confused on how to return data back to the previous activity. Perhaps because of previous experience with old styles like windows forms in dot-net.
Scenario: my simple android app starts with the MainActivity, showing some values in some units, e.g. 'you are 1.86 m tall', and having a tools icon in the menu bar. Clicking this icon starts the ToolsActivity where the user may select some settings, like wether she prefers american or metric units, e.g. meter versus foot. When done, the user clicks the back arrow in the top bar to return to the MainActivity, that should show the values in the selected units.
This is how I store the current setting of the units system:
const val EXTRA_UNITSYSTEM = ".UNITSYSTEM"
class MainActivity : AppCompatActivity() {
var TheUnitSystem : String = "metric"
I found out how to use putExtra() in the intent to startActivity() in the MainActivity to start the ToolsActivity,:
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.tools -> {
val intent = Intent(this, ToolsActivity::class.java)
intent.putExtra(EXTRA_UNITSYSTEM, TheUnitSystem)
startActivity(intent)
and getStringExtra() to get the current value of TheUnitSystem in the ToolsActivity so the radiobutton can be initialized to the current setting:
class ToolsActivity : AppCompatActivity() {
var TheUnitSystem: String = ""
override fun onCreate(savedInstanceState: Bundle?) {
...
this.TheUnitSystem = intent.getStringExtra(EXTRA_UNITSYSTEM)!!
This actually works, but leaves the problem of how to return the data, which is possibly changed to point at another units system, back to the MainActivity when the ToolsActivity ends (or stops, or finishes, or gets destroyed...)
Initially, my guess was that intents are used to go to another activity, and that it needs another intent to go back to the previous activity.
Another guess was that you use EXTRA data on the intent to get data to ToolsActivity, so somehow the EXTRA data is also to be used to get data back to the MainActivity.
Both guesses seem to be naive.
Then I found out about starting the ToolsActivity with startActivityForResults(), as this is designed to get a result back from the second activity. However, the stories that I kind of grasp are in java, and the stories from developer.android.com that use kotlin are much more abstract and seem to describe yet different methods.
Can someone point me at a basic kotlin example for returning a simple String back to MainActivity, preferably using startActivityForResults() ?
From programming in simple Windows apps, I would guess that it would be even simpler to use application global (static) data. If that were possible, we should not need EXTRA stuff and special ForResult() methods, so perhaps this is also a dead end street?
extra info:
What may make my simple project a bit special is that I don't have a button in the layout of the second activity for going back to the MainActivity. The second activity is started from clicking a MenuItem on the Toolbar widget, defined in the res/menu/menu_main.xml layout. The second activity is shown with a back arrow in the top bar which is not in the layout of the second activity. Advantage is that it is really a Settings screen. Disadvantage is that it is not a plain normal activity where you put a back button in the layout like in most coding examples.
You are on the right track, thinking about using startActivityForResults and I'm guessing all that is missing is connecting the original activity to receive results.
In the original activity you need to override onActivityResult like this:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// If you have multiple activities returning results then you should include unique request codes for each
if (requestCode == <pick a code>) {
// The result code from the activity started using startActivityForResults
if (resultCode == Activity.RESULT_OK) {
}
}
}
Then, in the activity you want to return information from, do something like this:
val intent = Intent()
intent.putExtra("ActivityResult", "<Data to return>")
setResult(RESULT_OK, intent)
finish()
This is one example, but they are all similar: Example
In my activity#1 I create my viewModel like this:
val viewViewModelProvider = ViewModelProviders.of(this)
bluetoothPageViewModel = viewViewModelProvider.get(BluetoothPageViewModel::class.java)
Nice it's work fine.
But I need when forward to another activity (activity#2) to destroy or clear all data in bluetoothPageViewModel.Because when I return (press back button) to activity#1 I need all data in bluetoothPageViewModel to be in init values.
Is it possible.
I made a new project using navigation drawer that android gives me as builtin and then i added my menus in the navigation Drawer and then made another activity that is empty activity and made a button over there of getStarted and on the click listening i made an intent of the mainActivity that has the navigation drawer and then assigned this to the getstarted button to startActivity(mainIntent) but its not working, I have done many R&D but didn't work at all and i am getting following errors.
I have tried all the discussion over here
but nothing is working in my case
class WelcomeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_welcome)
getStartedBtn.setOnClickListener {
val main = Intent(this,MainActivity::class.java)
startActivity(main)
}
}
I want to be routed to the main activity.In main activity I have used builtin toolbar or navigation drawer.
To access this from an outer scope (a class, or extension function, or labeled function literal with receiver) we write this#label where #label is a label on the scope this is meant to be from:
You should specify your scope. For more information you can check this link.
val main = Intent(this#WelcomeActivity, MainActivity::class.java)
startActivity(main)
Hope this works!
Try to change val main = Intent(this,MainActivity::class.java) to val main = Intent(WelcomeActivity.this,MainActivity::class.java)
Also make sure that getStartedBtn is correctly imported from xml file.
Do you imported getStartedBtn correctly and try to change the val main = Intent(this,MainActivity::class.java) to val main = Intent(applicatonContext,MainActivity::class.java) it thought this will help if you still facing any problem please post the error.