My question is that I have two activities, Activity A and Activity B. Activity A is the main activity, Activity B is only accessible by touching a notification.
I need to be able to touch a notification, which will open Activity B. After the user is finished with Activity B, they can press the back button or finish Activity B. When Activity B closes, Activity A should be shown. Currently, I close Activity A (which closes the entire app) and receive a push notification. I select the push notification which opens Activity B. When I close Activity B, Activity A is not shown and the entire app closes. I want Activity A to be there after finishing Activity B.
I am using Parse SDK notifications. In my custom ParsePushBroadcastReceiver I have the following code
public class MyReceiver extends ParsePushBroadcastReceiver
{
#Override
public void onPushOpen(Context context, Intent intent)
{
Intent i = new Intent(context, NotificationActivity.class);
i.putExtras(intent.getExtras());
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
I have it listed in the Android Manifest. My notification activity in manifest is
<activity
android:name=".NotificationActivity"
android:label="#string/app_name"
android:windowSoftInputMode="adjustPan"
android:screenOrientation="portrait"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity"/>
</activity>
In my notification activity I have the following code in onBackPressed
public class NotificationActivity extends ActionBarActivity
{...
#Override
public void onBackPressed()
{
if(NavUtils.getParentActivityName(this) != null)
{
Log.d(Constants.NAVIGATION_DEBUG, "Get Parent Activity NOT NULL");
NavUtils.navigateUpFromSameTask(this);
}
else
{
Log.d(Constants.NAVIGATION_DEBUG, "Get Parent Activity NULL");
super.onBackPressed();
}
}
...}
Surprisely, in the logs it states that it is NOT NULL, however it doesn't navigate back to Activity A if the app was closed prior to pressing the notification. Instead the app closes. I want Activity A to show whether the app was closed when the notification was pressed or whether the app is open. THANKS!
You can try the following method: put some boolean extra in your intent, and then start MainActivity. That extra will be the indicator of NotificationActivity start.
public class MyReceiver extends ParsePushBroadcastReceiver
{
#Override
public void onPushOpen(Context context, Intent intent)
{
Intent i = new Intent(context, MainActivity.class);
i.putExtras(intent.getExtras());
i.putExtra("IS_FROM_NOTIFICATION", true);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
Then in your MainActivity's onCreate() method, do something like this:
public class MainActivity extends ActionBarActivity {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (getIntent().getBooleanExtra("IS_FROM_NOTIFICATION", false)) {
startActivity(MainActivity.this, NotificationActivity.class);
}
}
// other initialization code
...
...
}
Then you don't need to override onBackPressed in your NotificationActivity.
for the temporary solution, you must start your MainActivity onBackPress() of NotificationActivity..
Just so everyone else who is running into this problem will have a solution which works I will post my solution to this migraine problem. The way I solved this problem is by using Fragments.
Explanation: In the activity oncreate I check the Intent to see if the user is in the application due to selecting a notification. If so I can transition to the notification fragment, if not I can transition to the main fragment. After the user is finished with the notification fragment the user will either press the back key on their phone, or press the back arrow in the ActionBar, so the developer has to handle both of those situations.
To handle the back button on the phone simply override onbackpressed in your activity and check to see if the user is in the notification fragment, if so then smoothly transition to your main fragment, without keeping the notification fragment in the backstack.
To handle the back button in the actionbar, override onoptionsselected in your fragment and when the user presses the back button (android.R.id.home) you simply smoothly transition to your mainfragment.
If you are also handling overriding your activity onnewintent, simply pass in a boolean to your fragment letting your notification fragment know whether it has been opened from onnewintent or oncreate from your activity.
If your notification fragment is opened from onnewintent, you wouldn't want to transition to your mainfragment, since your mainfragment would already be open and in the backstack. So you would simply POP the fragments backstack, or let the android system automatically handle proper navigation.
It's a lot of work, but it is a solution, and in theory this solution should work across all Android API's from GINGERBREAD - LOLLIPOP, as long as the user is using Fragments, and the user is using a custom animation to transition between fragments.
The key is using a custom fragment transition from the notification fragment back to the main fragment ( or in the case of onnewintent, whatever fragment came before it) to give the impression that the user is going BACKWARDS from notification fragment to main fragment. If anyone else comes with a better solution... I am all ears.
Use TaskStackBuilder in Android its the same you need.
Related
I have been developing this application for quite a while now and came up with this bug in the app. Initially when the application loads for the first time, it starts from the home activity. Then the user interaction will navigate application to activity1 which uses fragment1. The fragment1 has a button. If the user clicks on that button,the activity1 calls finish() and loads activity2 by calling startActivity().Also when the user clicks on the hardware back button from the fargment1, the application returns to the Home activity.
So the real issue here is that, when I am at activity2 and finish() the activity the application will show the Home activity. But i want to completely close the application on pressing back from the activity2.
The following is the code which starts the activity2 form the fragment1:
Intent intent = new Intent(context, Activity2.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
((Activity)context).finish();
I get that the Home activity has never been finish()-ed.
So how do I destroy the activity1 and home activity on navigating to the activity2, and make the activity2 only remaining activity in the application?
Thanks in advance.
Ok so this is what i did (according to #seema):
in the home activity i registered a broadcastreciever in OnCreate() method of the activity.
IntentFilter filter = new IntentFilter();
filter.addAction("com.example.CUSTOM_INTENT");
registerReceiver(receiver, filter);
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("==>","Broadcast Recieved.");
finish();
}
};
public void finish() {
super.finish();
};
then in the activity2 i send out the broadcast
Intent local = new Intent();
local.setAction("com.example.CUSTOM_INTENT");
sendBroadcast(local);
This worked out as I required it to be.
Thus any activity can be destroyed from any other activity.
First approach: use start activity for result instead of start activity and finish it in onActivityResult.
Second apparoach: Register a local broadcast in each required activity and in its receiver, finish it. From activtity2 send this broadcast. This will finiah all those activities in stack having that broadcast registered. I have used this approach for Logout implementation in many apps and it works well.
I have two activities, A and B. I have a button in A,when pressed starts a new intent to B.
However whenever I press the back button while in B, than click the button again is restarts Activity.I do not want to do that,I want it to resume Activity B.
Actually I am doing some networking in Activity B and I want to save unless the user wants to refresh.
How can I do it? Thanks in advance
Use
#Override
public void onBackPressed(){
moveTaskToBack(true);
}
Hope, It must help you
You need to Overrider the
onBackPressed
method and start there the activity like this:
#Override
public void onBackPressed() {
Intent intent = new Intent(this, activityA.class);
startActivityForResult(intent, PICK_CONTACT_REQ);
}
This sounds like you need to rethink your architecture. You say:
Actually I am doing some networking in Activity B and I want to save
unless the user wants to refresh.
If this is the case, you probably don't want to do your networking in ActivityB. Maybe you should start a Service from ActivityB to do the networking. The service and the activity can communicate with each other so that the activity can keep the user up-to-date about the state of the networking. If the user presses BACK in ActivityB, it can finish (as usual) and return to ActivityA. In this case, the Service is still managing the networking. When the user again starts ActivityB (from ActivityA), the new instance of ActivityB can communicate with the service to see if there is any networking going on, and if so it can get the current status of that networking or start it or stop it or whatever.
I guess I'm too late but I had a similar problem.
I had two activities A,B and a next button in A.
Whenever I tried to do: A->press next button ->B->press back button->A->press next button->B, B screen got destroyed when I pressed the back button. So when I came back to B for the second time it was a newly created B (all the information I had put in was gone).
So it was like A->B->A->new B when I just wanted to go back to the original B! :(
So what I did was, in activity B, I overrode the onBackPressed() function so it doesn't destroy the activity. I also set the flag so that if there is a A activity already running, it would just pull it up to the front instead of creating a new A activity.
#Override
public void onBackPressed() {
Intent intent = new Intent(getApplicationContext(), ActivityA.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
}
Then, for the onclicklistener function for the next button in activity A, I set the flags similarly.
public void onClickNextbutton(View v){
Intent intent = new Intent(getApplicationContext(), ActivityB.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
}
I have a 'list' activity which starts an 'article' activity when clicked.
I also have push notifications which opens the 'article' activity directly.
I changed the back button behavior in the 'article' activity to start the 'list' activity, when coming from a notification so that the user will go back to the article list.
The problem is when the app is already opened in the background and I open a notification - it just brings it back to front.
What I want to achieve is open the right article when clicking a notification and going back to the 'list' activity, without having the possibility the the list activity will be open twice.
I tried to separate the 'article' task and create new task in the notification intent but then it would open separate 'list' activities when opening multiple notifications and clicking back.
What is the correct way to define the activities' tasks and intent flags to achieve my goal?
EDIT:
Manifest part:
<activity android:name="ListFeed" android:configChanges="orientation|screenLayout" android:launchMode="singleInstance" android:screenOrientation="unspecified"
android:taskAffinity="com.app.MyTask"></activity>
<activity android:name="Article" android:launchMode="standard" android:configChanges="orientation|screenLayout" android:screenOrientation="unspecified"
android:taskAffinity="com.app.MyTask"></activity>
Notification intent:
Intent notificationIntent = new Intent(context, Article.class);
PendingIntent contentIntent = PendingIntent.getActivity(context, notificationID, notificationIntent, PendingIntent.FLAG_ONE_SHOT);
Thanks!!
what i got from your question is that
1) you have listActivity A
2) ArticalActivity B.
i) And first you want to open Activity A whenever back from B, Correct? for that you can use dispatchKeyEvent, listen to Back button event and start activity A. or by using below code
#Override
public void onBackPressed() {
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
ii) you want to open only single instance of Activity A(list). for this you can basically use
launchMode in Activity A's Manifest declration as singleInstance.
android:launchMode="singleTask"
you can read docs for launch mode
let me know if i missed anything.
I see that you are playing around with launchModes and excludeFromRecents and this isn't a good thing. The standard behaviour of Android should do pretty much what you want.
To verify this I've created a simple 3-activity application that contains a MainActivity, a ListActivity and an ArticleActivity. I'm not using any non-standard launch modes and I'm not setting any Intent flags (except in onBackPressed() see below). The Main Activity creates and posts a notification to display a specific Article. The MainActivity starts the ListActivity. Each element of the ListActivity starts an Intent for the ArticleActivity and passes some information in EXTRAS so that the ArticleActivity knows which article to display.
In order to have the behaviour you described (ie: returning from the ArticleActivity to the ListActivity after starting the app from a notification, even if the app was not running), I've done what Ankit has suggested (ie: override onBackPressed() in ArticleActivity) like this:
#Override
public void onBackPressed() {
// Return to ListActivity
Intent intent = new Intent(this, ListActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
// Finish this activity (in case the ListActivity wasn't already in the stack)
finish();
}
I used FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP because this will not recreate the ListActivity if it already exists in the activity stack (ie: it will go back to the same instance).
I had to add the finish() call, because if the app was not running in the background and the user started it from the notification, the ListActivity would be created and put on top of the ArticleActivity. Then when the user pressed "back" to leave the ListActivity, the ArticleActivity would be exposed underneath. Adding finish() here makes the ArticleActivity go away so that pressing "back" from the ListActivity goes back to wherever it came from.
If you want me to send you the code, just let me know.
I have a foreground service that keeps a connection open with the server as long as the user is logged into the application. This is so that the connection is kept alive and can receive messages directly from the server even when the application has been sent into the background by the user pressing Home.
The application has a number of Activities, any of which could be the active one when it is sent into the background.
I would like to allow the user to click on the notification to restore the current Activity. I understand how to restore a particular activity, but wondered if there is a way to restore the last Activity that the user was on? Of course I could keep track of the the last one, and then call that from the Notification callback, but thought there might be a way at a task level?
Thanks for any advice you can offer.
What you need is just a simple Activity that does nothing. Here is an example:
public class NotificationActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Now finish, which will drop the user in to the activity that was at the top
// of the task stack
finish();
}
}
Set up your notification to start this activity. Make sure that in the manifest the task affinity of this activity is the same as the task affinity of the other activities in your application (by default it is, if you haven't explicitly set android:taskAffinity).
When the user selects this notification, if your application is running, then the NotificationActivity will be started on top of the topmost activity in your application's task and that task will be brought to the foreground. When the NotificationActivity finishes, it will simply return the user to the topmost activity in your application (ie: wherever the user left it when it went into the background).
This won't work if your application isn't already running. However, you have 2 options to deal with that:
Make sure the notification isn't present in the notification bar when your application is not running.
In the onCreate() method of the NotificationActivity, check if your application is running, and if it isn't running call startActivity() and launch your application. If you do this, be sure to set the flag Intent.FLAG_ACTIVITY_NEW_TASK when starting the application so that the root activity of the task is not NotificationActivity.
Works very well, thanks David! The following class checks if the application is already running and if not, starts it before finishing (as suggested by David in option 2).
public class NotificationActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// If this activity is the root activity of the task, the app is not running
if (isTaskRoot())
{
// Start the app before finishing
Intent startAppIntent = new Intent(getApplicationContext(), MainActivity.class);
startAppIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(startAppIntent);
}
finish();
}
}
There is a simpler solution that does not require the extra activity. See this post for details. Basically, the notification starts the (possibly existing) task the same way it is started when you click the launcher icon while the app ist in the background.
My solution, which emulates the behaviour of the launcher (bringing up the task to the foreground):
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setClassName(MyApplication.class.getPackage().getName(), MainActivity.class.getName());
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
This works, no doubts about it but the problem is when you set your intent as ACTION_MAIN. Then you will not be able to set any bundle to the intent. I mean, your primitive data will not be received from the target activity because ACTION_MAIN can not contain any extra data.
Instead of this, you can just set your activities as singleTask and call your intent normally without setting ACTION_MAIN and receive the intent from onNewIntent() method of your target activity.
But be aware if you call, super.onNewIntent(intent); then a second instance of the activity will be created. Just don't call super method.
I combined David Wasser's and Raginmari's solution by doing that approach to the root activity of your app then it will work for both cases when your app was already started or haven't been started.
public class YourRootActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (!isTaskRoot()) // checks if this root activity is at root, if not, we presented it from notification and we are resuming the app from previous open state
{
val extras = intent.extras // do stuffs with extras.
finish();
return;
}
// OtherWise start the app as usual
}
}
I would like to put a notification with an intent.
My intent is basically action = DEFAULT and category = LAUNCHER in order to bring the activity that was launched into the front.
When the app is not shown, there is no problem, the intent works perfectly and launches the last activity seen but when there is already an activity launched, onNewIntent is not called (activity is in singleTop mode).
I'm wondering how to relaunch the app from an intent to the last activity seen and call onNewIntent when the activity is already launched.
the problem is that your activity is defined as
android:launchMode="singleTop"
when there is already an activity
launched, onNewIntent is not called
implement the onDestroy method::
#Override
public void onDestroy(){
super.onDestroy();
}