Removing extras from passed-in Intent - android

I have a search screen which can be launched from clicking on a "name" field of another screen.
If the user follows this workflow, I add an extra to the Intent's Extras called "search". This extra uses the text populating the "name" field as its value. When the search screen is created, that extra is used as a search parameter and a search is automatically launched for the user.
However, since Android destroys and recreates Activitys when the screen rotates, rotating the phone causes an auto-search again. Because of this, I'd like to remove the "search" extra from the Activity's Intent when the initial search is executed.
I've tried doing this like so:
Bundle extras = getIntent().getExtras();
if (extras != null) {
if (extras.containsKey("search")) {
mFilter.setText(extras.getString("search"));
launchSearchThread(true);
extras.remove("search");
}
}
However, this isn't working. If I rotate the screen again, the "search" extra still exists in the Activity's Intent's Extras.
Any ideas?

I have it working.
It appears getExtras() creates a copy of the Intent's extras.
If I use the following line, this works fine:
getIntent().removeExtra("search");
Source code of getExtras()
/**
* Retrieves a map of extended data from the intent.
*
* #return the map of all extras previously added with putExtra(),
* or null if none have been added.
*/
public Bundle getExtras() {
return (mExtras != null)
? new Bundle(mExtras)
: null;
}

While #Andrew's answer may provide a means for removing a specific Intent extra, sometimes it is necessary to clear ALL of the intent extras and, in this case, you will want to use
Intent.replaceExtras(new Bundle())
Source code of replaceExtras:
/**
* Completely replace the extras in the Intent with the given Bundle of
* extras.
*
* #param extras The new set of extras in the Intent, or null to erase
* all extras.
*/
public #NonNull Intent replaceExtras(#NonNull Bundle extras) {
mExtras = extras != null ? new Bundle(extras) : null;
return this;
}

The problem can be solved using extra flag which is persistent during destroys and recreations. Here is the narrowed down code:
boolean mProcessed;
#Override
protected void onCreate(Bundle state) {
super.onCreate(state);
mProcessed = (null != state) && state.getBoolean("state-processed");
processIntent(getIntent());
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
mProcessed = false;
processIntent(intent);
}
#Override
protected void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
state.putBoolean("state-processed", mProcessed);
}
protected void processIntent(Intent intent) {
// do your processing
mProcessed = true;
}

Kotlin
activity?.intent?.removeExtra("key")

Related

Intent returning null after leaving app and coming back

If im staying in the app, this works fine
Click button to take me to new activity:
intent.putExtra("invite_id", invite_id);
startActivity(intent);
Receiving Activity:
Bundle b = getIntent().getExtras(); //invite id is in here
Now here is the weird part. If I am in the app, then click home button to leave the app and go to the native contacts app and save ANYTHING (like edit a name or number...the problem only occurs if I actually save something), then go to recent apps and open up my app from there... now if I click the button to launch my intent to take me to a new activity, the receiving activity returns a null bundle
Bundle b = getIntent().getExtras(); //returns null
Why could this be happening?
String b = getIntent().getStringExtra("invite_id");
Intent extras are always persisted across activity death and recreation. So, if you're stashing that extra value, it will continue to be there if you are resuming the app with the recent app switcher.
I would verify that you are stashing values in the intent.
simply that means that your activity being recreated so you have to options to avoid that:
Avoid recreating your activity by setting this to your Activity within the manifest
android:configChanges="keyboardHidden|orientation|screenSize|locale"
Or by saving the instance of the id you received by doing such:
#Override protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("myId",myId);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
if (savedInstanceState == null) {
myId = getIntent().getExtras().getString("myId");
}
}

Android cant tell intents apart

In my main activity (where everything happens in my application) I call a variety as of now just two other activities which end up calling back to my MainActivity via button press. How do I distinguish between these two Intents back to my MainActivity? I have seperate operations I want to prefrom based on things I did back in the two seperate activites.
Heres what I tried:
Intent intent = getIntent();
String s_message = intent.getStringExtra(AppSettings.EXTRA_MESSAGE);
String f_message = intent.getStringExtra(ViewFavorites.EXTRA_MESSAGE);
if(s_message != null) {
//do something
} else if (f_message != null) {
//do something
}
But when I run my application I find when exiting the two activities that they are prefroming the methods I do not wish them to...am I going about this wrong?
What I do is simply set an Extra in my passing Intent then compare that. Something like this. When creating the Intent add an Extra to compare to
intent.putExtra("source", "appSettings");
then in your Activity check what that value is
Intent intent = getIntent();
String source = intent.getStringExtra("source"); // get that value here
if(s_message != null) {
if ("appSettings".equals(source)){
//do something
} else if (viewFavorites.equals(source)) {
//do something else
}
}
You could use variations of this as far as how you assign the Extra but this is a simple example that works well for me, especially when there are just a few Activites that will be calling this one.
Set a different ACTION on each intent, then use if(getIntent().getAction().equals(ACTION)) to distinguish between intents.
public class MainActivity extends Activity {
public static final String ACTION_ONE = "com.yourpackage.ACTION_ONE";
public static final String ACTION_TWO = "com.yourpackage.ACTION_TWO";
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if(intent.getAction() != null){
if(intent.getAction.equals(ACTION_ONE){
//DO SOMETHING
} else if (intent.getAction.equals(ACTION_TWO){
//DO SOMETHING
}
}
}
.....
}
Then when you start your main activity with an intent:
Intent intent = new Intent(MY_CURRENT_CONTEXT, MainActivity.class); //Or MainActivity subclass
add
intent.setAction(ACTION_ONE);
or whichever action is specific to what your intent is trying to accomplish.

onResume causing problems on start up

I have an activity that allows the user to start a second activity. The second activity has a list of items which I add to an array list. When I return to the previous activity I want to display the size of the array list.
However I am having a problem with onResume(). It is called when my first activity is created and as a result generates an error as the array list does not exist when it is first launched!
onResume():
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
getIntentData();
calcSubTotal(orderData);
}
getIntentData():
public void getIntentData(){
b = new Bundle();
b = getIntent().getExtras();
orderData = b.getParcelable("order");
Toast.makeText(this.getApplicationContext(), orderData.size(), Toast.LENGTH_LONG).show();
}
onCreate() of second activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_starters);
createTestData();
b = new Bundle();
orderData = new MenuItemList();
adapter = new MenuItemArrayAdapter(this, starters);
this.setListAdapter(adapter);
}
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Toast.makeText(this.getApplicationContext(), l.getItemAtPosition(position).toString() + " clicked", Toast.LENGTH_LONG).show();
//add clicked item to orderData....
MenuItem m = (MenuItem)l.getItemAtPosition(position);
//create new item
orderData.add(m);
}
Any idea how I might be able to control this?
ERROR:
java.lang.RuntimeException: Unable to resume activity {com.example.waitronproto3/com.example.waitronproto3.SectionsActivity}: java.lang.NullPointerException
I think you may want to have a look at startActivityForResult instead, when you're starting your second Activity. It'll allow your second activity to return a result back to your first activity. You can read up on it at the Activity documentation, specifically the "Starting Activities and Getting Results" section of the document.
Edit: By the looks of your code - nothing you're doing is either storing a bundle from the second activity and sending it back to the first. So you'll never get the proper Bundle data in your first activity. As suggested, look into startActivityForResult to launch your second activity with. This will allow you to return data back into your first activity with ease.
However I am having a problem with onResume(). It is called when my first activity is created and as a result generates an error as the array list does not exist when it is first launched!
I recommend changing getIntentData() to check if the appropriate data exists first:
public void getIntentData(){
Intent intent = getIntent();
if(intent != null && intent.hasExtra("order")) {
orderData = b.getParcelable("order");
Toast.makeText(this.getApplicationContext(), orderData.size(), Toast.LENGTH_LONG).show();
calculateSubTotal(order);
}
}
And update onResume():
#Override
protected void onResume() {
super.onResume();
getIntentData();
}
(Though you could simply put getIntentData() in onResume() now.)
Your onResume() will be called after onCreate() according to the Android Lifecycle so you will want to check that the data is not null before trying to use it.
`if(intentData != null)
//do something`

How to use onSavedInstanceState example please

I'm confused when it comes down to saving a state. So I know that onSaveInstanceState(Bundle) is called when the activity is about to be destroyed. But how do you store your information in it and bring it back to its original state in onCreate(Bundle savedInstanceState)? I don't understand how this bundle will restore information. It would be helpful if someone can provide an example.
The Dev guide doesn't do a good job of explaining this.
public class Conversation extends Activity {
private ProgressDialog progDialog;
int typeBar;
TextView text1;
EditText edit;
Button respond;
private String name;
private String textAtView;
private String savedName;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.dorothydialog);
text1 = (TextView)findViewById(R.id.dialog);
edit = (EditText)findViewById(R.id.repsond);
respond = (Button)findViewById(R.id.button01);
if(savedInstanceState != null){
savedInstanceState.get(savedName);
text1.setText(savedName);
}
else{
text1.setText("Hello! What is your name?");
respond.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
name = edit.getText().toString();
text1.setText("Nice to meet you "+ name);
}
});
}
}
#Override
public void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
outState.putString(savedName, name);
}
}
The Bundle is a container for all the information you want to save. You use the put* functions to insert data into it. Here's a short list (there are more) of put functions you can use to store data in the Bundle.
putString
putBoolean
putByte
putChar
putFloat
putLong
putShort
putParcelable (used for objects but they must implement Parcelable)
In your onCreate function, this Bundle is handed back to the program. The best way to check if the application is being reloaded, or started for the first time is:
if (savedInstanceState != null) {
// Then the application is being reloaded
}
To get the data back out, use the get* functions just like the put* functions. The data is stored as a name-value pair. This is like a hashmap. You provide a key and the value, then when you want the value back, you give the key and the function gets the value. Here's a short example.
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putString("message", "This is my message to be reloaded");
super.onSaveInstanceState(outState);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
String message = savedInstanceState.getString("message");
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
}
Your saved message will be toasted to the screen.
One major note that all new Android developers should know is that any information in Widgets (TextView, Buttons, etc.) will be persisted automatically by Android as long as you assign an ID to them. So that means most of the UI state is taken care of without issue. Only when you need to store other data does this become an issue.
From Android Docs:
The only work required by you is to
provide a unique ID (with the
android:id attribute) for each widget
you want to save its state. If a
widget does not have an ID, then it
cannot save its state
A good information: you don't need to check whether the Bundle object is null into the onCreate() method. Use the onRestoreInstanceState() method, which the system calls after the onStart() method. The system calls onRestoreInstanceState() only if there is a saved state to restore, so you do not need to check whether the Bundle is null
Store information:
static final String PLAYER_SCORE = "playerScore";
static final String PLAYER_LEVEL = "playerLevel";
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current game state
savedInstanceState.putInt(PLAYER_SCORE, mCurrentScore);
savedInstanceState.putInt(PLAYER_LEVEL, mCurrentLevel);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
If you don't want to restore information in your onCreate-Method:
Here are the examples: Recreating an Activity
Instead of restoring the state during onCreate() you may choose to implement onRestoreInstanceState(), which the system calls after the onStart() method. The system calls onRestoreInstanceState() only if there is a saved state to restore, so you do not need to check whether the Bundle is null
public void onRestoreInstanceState(Bundle savedInstanceState) {
// Always call the superclass so it can restore the view hierarchy
super.onRestoreInstanceState(savedInstanceState);
// Restore state members from saved instance
mCurrentScore = savedInstanceState.getInt(PLAYER_SCORE);
mCurrentLevel = savedInstanceState.getInt(PLAYER_LEVEL);
}
Basically onSaveInstanceState(Bundle outBundle) will give you a bundle.
When you look at the Bundle class, you will see that you can put lots of different stuff inside it. At the next call of onCreate(), you just get that Bundle back as an argument.
Then you can read your values again and restore your activity.
Lets say you have an activity with an EditText. The user wrote some text inside it.
After that the system calls your onSaveInstanceState().
You read the text from the EditText and write it into the Bundle via Bundle.putString("edit_text_value", theValue).
Now onCreate is called. You check if the supplied bundle is not null. If thats the case,
you can restore your value via Bundle.getString("edit_text_value") and put it back into your EditText.
This is for extra information.
Imagine this scenario
ActivityA launch ActivityB.
ActivityB launch a new ActivityAPrime by
Intent intent = new Intent(getApplicationContext(), ActivityA.class);
startActivity(intent);
ActivityAPrime has no relationship with ActivityA.
In this case the Bundle in ActivityAPrime.onCreate() will be null.
If ActivityA and ActivityAPrime should be the same activity instead of different activities,
ActivityB should call finish() than using startActivity().
If Data Is not Loaded From savedInstanceState use following code.
The problem is url call is not to complete fully so, check if data is loaded then to show the instanceState value.
//suppose data is not Loaded to savedInstanceState at 1st swipe
if (savedInstanceState == null && !mAlreadyLoaded){
mAlreadyLoaded = true;
GetStoryData();//Url Call
} else {
if (listArray != null) { //Data Array From JsonArray(ListArray)
System.out.println("LocalData " + listArray);
view.findViewById(R.id.progressBar).setVisibility(View.GONE);
}else{
GetStoryData();//Url Call
}
}

Differentiating between an Activity launch from home screen or from another activity from App

I need to know a generic way to distinguish between a call of activity from launcher and a call from another activity from inside my app, or a BACK on the activity stack
Anyone? this is bugging me for quite a while now and i need to put it to rest...
Thanks in advance
JQCorreia
In the onCreate of your Activity, call getIntent(). If the Activity is started from the launcher (home screen) the values for getAction() will be android.intent.action.MAIN and the getCategories() will return a set which will contain the android.intent.category.LAUNCHER category.
If the activity is started from elsewhere these values may be null.
In addition to #advantej's answer, you can extend each start-call to that activity adding an extra to the starting intent (e.g. intent.putExtra("caller", this.getClass().getSimpleName());
In the activity's onCreate method you can check then what #advantej suggests.
If the initiator is not the home-screen icon, than you can check further if the intent.hasExtra("caller") returns true, and if so, what is it.
You can find it out from intent flag.
step 1:
Intent intent = getIntent();
int flag = intent.getFlag();
step 2:
if flag = Intent.FLAG_ACTIVITY_NEW_TASK
launch from other app or activities
else
launch from home page
in 2 cases the onRestart(); called, 1.when activity come from background, 2.when the user reach the activity by back button then sample solution:
use onBackPressed() function to set a flag.. so u know that onRestart called becouse of back button press...
in onRestart () check the flag and reset it and....
Based on advantej's answer, here is a full example that also hides the UP button if the activity was launched from a launcher icon:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sell);
/**
* If this activity was started from launcher icon, then don't show the Up button in the action bar.
*/
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
Intent intent = getIntent();
Set<String> intentCategories = intent.getCategories();
boolean wasActivityStartedFromLauncherIcon = Intent.ACTION_MAIN.equals(intent.getAction()) && intentCategories != null && intentCategories.contains(Intent.CATEGORY_LAUNCHER);
boolean showUpButton = !wasActivityStartedFromLauncherIcon;
actionBar.setDisplayHomeAsUpEnabled(showUpButton);
}
}
Here's convenience method so you don't need to write it yourself:
protected boolean isStartedByLauncher() {
if (getIntent() == null) {
return false;
}
boolean isActionMain = Intent.ACTION_MAIN.equals(getIntent().getAction());
Set<String> categories = getIntent().getCategories();
boolean isCategoryLauncher = categories != null && categories.contains(Intent.CATEGORY_LAUNCHER);
return isActionMain && isCategoryLauncher;
}
The simplest approach that I can think of would be to pass a flag while launching the activity from your own activities. You should also check if the activity was created or resumed, this can be done by setting a boolean in the onCreate method, and then checking it onResume.
Below is the code you can use (not tested):
Activity in which you want to check (say MainActivity.class):
Boolean onCreateCalled = false;
Boolean calledFromAppActivities = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
onCreateCalled = true;
Bundle mainData = getIntent().getExtras();
if (mainData != null) {
if (getIntent().hasExtra("call_from_own_activity")) {
calledFromAppActivities = true;
}
}
.....
}
#Override
protected void onResume() {
super.onResume();
if (onCreateCalled && !calledFromAppActivities) {
// The app was not called from any of our activities.
// The activity was not resumed but was created.
// Do Stuff
}
// To stop it from running again when activity is resumed.
onCreateCalled = false;
....
}
When calling MainActivity from other activities, use the code below:
private void call_main () {
Intent i = new Intent(getApplicationContext(), MainActivity.class);
i.putExtra("call_from_own_activity", true);
startActivity(i);
}

Categories

Resources