android handle activity launching - android

I have the following situation:
when I launch my app the following scenario takes place:
I start the activity StartActivity
If I never did a login (I know it based on some shared preference) launch LoginActivity and show a login layout (username and password)
If I already did a login I start LoginActivity anyway, but I use (crypted) data saved on shared preferences without asking again credentials to the user.
When the login succeed I launch the activity StartActivity
Now in my manifest file I have the following IntentFilter for MainActivity:
<intent-filter android:label="Conio" >
<action android:name="android.intent.action.VIEW" />
<data android:scheme="myprotocol" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
And everything works well with a big "BUT":
everything works only when my app is already opened: my app is brought to front, the onCreate method is called and with getIntent.getData() I can access the URI which started my app.
THE BIG PROBLEM:
WHAT I NEED IS :
When my app is not already started everything should begin from StartActivity as per previous explanation
When my app is running the behaviour must be the current one
To accomplish this I made the following attempt:
I applied that IntentFilter to a new Activity , in this Activity's onCreate method I put the following code
Intent intent = getPackageManager().getLaunchIntentForPackage("my.package.name");
if (intent != null) {
intent.setData(intent.getData());
intent.putExtra("test", "test");
startActivity(intent);
finish();
}
but what happens here is that when app is not running the app correctly starts from StartActivity, but if if the app is already running, MainActivity is brought to front but I can't access the intent extras, in fact this code do not works
#Override
protected void onResume() {
super.onResume();
//this code returns null
String test=getIntent.getStringExtra("test");
}
how can I make all this to work?

if the app is already running, MainActivity is brought to front but I can't access the intent extras:
To get Intent deliver when Activity is running either use LocalBroadcastManager sending new Intent to running Activity instead of calling startActivity method.
OR
Without LocalBroadcastManager add FLAG_ACTIVITY_SINGLE_TOP in Intent or add android:launchMode="singleTask" in AndroidManifest.xml when starting MainActivity :
intent.setData(intent.getData());
intent.putExtra("test", "test");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
and override onNewIntent method in MainActivity:
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
//get data from intent
}

Related

Android - reopen main activity from notification

Launcher activity for my app is called LaunchActivity. Inside this activity I check if the user is logged. If yes, then a network call is made to validate the user and if everything is fine MainActivity is started. If user runs app for first time or user validation fails, LoginActivity runs.
Inside LaunchActivity there is function that runs appropriate activity:
private void start(Class<? extends Activity> startActivity) {
Intent intent = new Intent(this, startActivity);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
finish();
startActivity(intent);
}
App is receiving notifications. After notification click i want to open app or bring it top if it is on background.
Code responsible for notification intent:
Intent intent = new Intent(context, LaunchActivity.class);
intent.putExtra("SHOW_NOTIFICATION_LIST", "");
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
PendingIntent notificationIntent = PendingIntent.getActivity(
context,
0,
intent,
0);
builder.setContentIntent(notificationIntent);
Notification notification = builder.build();
The problem is that with my current implementation after click, LaunchActivity is reopened even when MainActivity is on background. I can not figure out how to make the pending intent reopen just MainActivity (without LaunchActivity and valdation) if it is on background OR start LaunchActivity when there are no activity running on background ( app is not running ). I would be very grateful for any kind of help.
edit:
Activities declaration inside manifest:
<activity android:name=".activities.LaunchActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activities.MainActivity"
android:launchMode="singleTop"
android:screenOrientation="userPortrait" />
<activity
android:name=".activities.LoginActivity"
android:excludeFromRecents="true"
android:launchMode="singleTop"
android:noHistory="true"
android:screenOrientation="userPortrait"
android:windowSoftInputMode="adjustResize" >
</activity>
The reason for the absurd behavior is that because you are finishing the LaunchActivity when you call start()
private void start(Class<? extends Activity> startActivity) {
....
finish(); // This is the culprit
....
}
Remove finish() from start() and it will work fine as expected.
If you can't remove finish() because of your app behavior, then change your PendingIntent to launch MainActivity instead of LaunchActivity
Intent intent = new Intent(context, MainActivity.class);
And in MainActivity's onCreate() check if the user is already logged in . If not then navigate him to your LoginActivity.
Create a BroadcastReceiver which reacts to three different intents
ACTION_CREATED: set flag to true
ACTION_DESTROYED: set flag to false
ACTION_LAUNCH: if flag, start MainActivity, otherwise start LaunchActivity
(flag can be e.g. a boolean in sharedpreferences or a static field)
in MainActivity:
in onCreate: send ACTION_CREATED broadcast
in onDestroy: send ACTION_DESTROYEDbroadcast
The notification should send an ACTION_LAUNCHbroadcast.
Note: actions can be named as whatever you want. They also should start with you package name, so you don't interfere with other apps.

Hierarchy of launching activities in android

I have a confusion with how android starts its launcher activities.
If I am declaring a launcher activity in the manifest file like this
<activity android:name=".Activities.Home">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
and I have an Application class which calls an activity based on a check like
if(ParseUser.getCurrentUser() == null){
Intent intent = new Intent(context,Home.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}else{
Intent intent = new Intent(context,MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
which one has the precedence ? The manifest or the Application. What is the flow of events ? e.g. Application->Manifest (or) Manifest->Application (or) Application overrides Manifest ?
If I am receiving a notification in Android, my Application class is called. This makes the activities in the application class to be started, like shown above. Is there a way to detect who calls the Application class ? I mean whether the user starts it, or it starts from the notification which comes in ?
If there is a way to figure this out. How do I prevent the activity in the Application class to be called when I receive a notification ?
Thanks in advance.
Just pass the boolean extras from application and make the diffrence in the call from Application class and User Launch.
Intent intent = new Intent(context,Home.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("APP_CALL",True);
startActivity(intent);
Now in your Home activity Just check for APP_CALL if it is called from Application class then bool value will be true else false.
Also you can write in splash activity for checking login .

android: put login activity on top of the stack on each app start

I have a LoginActivity, that is started on the first app start. In The Manifest I defined for LoginActivity
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
So, if i close the app while any other Activity is active (e.g. MyActivity) and open it again, the last active activity MyActivity is shown.
Is it possible to somehow overlay the LoginActivity on top of the stack on each app start? And if the LoginActivity will finish, the old activity stack will be used?
Thank you!!
You can set your MyActivity to be your launcher activity. In the OnCreate method, you can check if the user is logged, and, if not, you can start the login activity.
In your Manifest, define your login as a child of de MyActivity as
android:parentActivityName="com.example.myfirstapp.MainActivity"
So, when the user finish the login activity, he will be redirected to the previous activity.
Many apps uses SharedPreferences to save the state of the user, and determine if he is logged or not.
to make that, when navigating from one activity to another, make the intent to the new activity as a NEW_TASK:
Intent intent;
intent = new Intent(v.getContext(), New_Activity.class); //replace v.getContext with getApplicationContext() if redirecting is not a result of pressing some Button
intent.putExtra("EXIT", false);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
then, on each activity override the onRestart() method with the following:
#Override
public void onRestart() {
Intent intent;
intent = new Intent(getApplicationContext(), Login_Activity.class); // be aware here, we navigate to Login activity without closing the old one
intent.putExtra("EXIT", false);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}

android : How to work around the fact that a singleInstance activity cannot expect to rely onActivityResult when starting subActivity?

I am making an android application, which has the following execution flow:
A service registers a PendingIntent with the AlarmManager
When the alarm is launched, a Receiver receives the intent, and (given some conditions) calls startsActivity() for my Main Activity, which in the manifest has been declared as android:launchMode="singleInstance". Note that for this call to work, the intent passed should have an Intent.FLAG_ACTIVITY_NEW_TASK
When started, Main Activity modifies itself a bit, and calls startActivityForResult for an Activity, which we'll call WebviewActivity (because it contains a webview, but that's besides the point)
When the user is done interacting with theWebViewActivity, setResult() and finish() are called on it, and one would expect for MainActivity.onActivityResult() to be called.
But of course this does not happen, as has been documented in many discussions here, the reason apparently being that an Activity launched from a singleInstance Activity, runs in a different Task.
A solution I think would be to have the WebActivity start the MainActivity instead.
The question is, is there a way to maintain onActivityResult being called at the right time? In that case, which aspects from the starting point of the execution flow should change?
Please note that MainActivity should not have multiple instances at the same time (it is basically an interface to the service) but if its launchMode is set to standard, the Receiver, because of the FLAG_ACTIVITY_NEW_TASK that is required, will do just that.
Manifest declaration of MainActivity:
<activity android:name=".activities.MainActivity"
android:label="#string/app_name"
android:launchMode="singleInstance"
android:configChanges="keyboardHidden|orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
android:uiOptions=”splitActionBarWhenNarrow”
</activity>
Receiver launches MainActivity by calling
onReceive(Context context, Intent intent)
{
intent.setClass(context, MainActivity.class);
int flag = Intent.FLAG_ACTIVITY_NEW_TASK;
intent.setFlags(flag);
context.startActivity(intent);
}
I use the following workaround for this problem:
Activity A is the caller
Activity B is the singleInstance activity from which I want the result
In activity A I register a broadcast receiver as following
PickReceiver receiver=new PickReceiver();
IntentFilter filter=new IntentFilter();
filter.addAction("ActivityA_pick");
registerReceiver(receiver,filter);
class PickReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
if(intent.getAction().equals("ActivityA_pick")){
//get data from intent extras
}
}
In ActivityB when it's time to send the data I use:
sendBroadcast("ActivityA_pick").putExtra("...data...");
finish();
This way i can get the result I want when I want a result from one of my own activities. If you want a result from the system or another app you can adjust this using a dummy activity that doesn't have launch mode singleInstance, have it start the activity for result and when it gets it onActivityResult it sends the broadcast to the caller.
Hope this helps
As the Main Activity is a single instance, is doing what it has been told.
So yes, you have to start the Main Activity from the Web Activity in order to be coherent with the tasks executions

Android Home activity launching a sub-activity

I am relatively new to Android programming. I have been given a task at work where I need to create a Custom Home activity launcher. I did a bit of research and found the example on the Android developer website (home sample). This was the start for my prototype.
The custom Home activity will need to require the user to authenticate (enter some simple credentials). My thought was to launch a sub-activity from the Home activity and pass back the results in the intent to the Home activity. I need to be able to capture information about these credentials and that information was going to be passed back to the Home activity. However, I have problems when trying this. I get a log in the LogCat that says the following: "Activity is launching as a new task, so canceling activity result."
I am aware of the startActivityForResult method, but that does not seem to be working for me. Here is where I launch the activity from my Home activity:
#Override
protected void onResume() {
super.onResume();
bindRecents();
Intent iii = new Intent(this, Login.class);
startActivityForResult(iii, STATIC_LOGIN_INTEGER_VALUE);
}
When that code executes, I get the above mentioned log from the ActivityManager tag.
My Login activity has a button that the user will hit once they have entered their credentials. If the credentials are good, then I try to do the following (I put in several logs so that I could try to figure out what is going on):
public void onClick(View src) {
// check for authentic credentials
if(IsValid())
{
Intent loginAuth = new Intent("Login");
loginAuth.putExtra("userRole", userRole);
Log.d("LOGIN", "Setting result...");
if (getParent() == null) {
Log.d("LOGIN", "Parent was null");
setResult(Activity.RESULT_OK, loginAuth);
}
else {
Log.d("LOGIN", "setting result on parent...");
getParent().setResult(Activity.RESULT_OK, loginAuth);
}
Log.d("LOGIN", "Finishing the activity");
finish();
}
}
I defined these activities in my manifest file as the following:
<activity android:name="Home"
android:theme="#style/Theme"
android:launchMode="singleInstance"
android:stateNotNeeded="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name="Login"
android:label="Login"
android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
</intent-filter>
</activity>
I was playing around with the intent filter on the Login activity. I had originally had it set to nothing. I also had the launchMode blank (which defaults to standard I believe). I have played around with most of these options and nothing seems to change the fact that the ActivityManager seems to want to launch the activity as a new task and wants to ignore the returned intent (which I need).
The problem is that you declared your activities with launchMode="singleInstance", so Android creates a new task (i.e. a new process) when it launches the Login activity. Activities in different tasks cannot return results to each other. From the Activity.startActivityForResult() docs:
For example, if the activity you are
launching uses the singleTask launch
mode, it will not run in your task and
thus you will immediately receive a
cancel result.
singleInstance is like singleTask but even more restrictive. Try removing the launchMode attribute from your manifest so that the Login activity will launch in the same task, then using FLAG_ACTIVITY_NEW_TASK when you need to launch a different activity in a separate task.
Barry
The documentation says
For example, if the activity you are launching uses the singleTask
launch mode, it will not run in your task and thus you will
immediately receive a cancel result.
THIS IS WRONG.
You can use the singleTask just fine, what you can't use is singleInstance.
Let's return to the problem. What is the problem?
The problem is that when you call startActivityForResult() on an activity, the caller activity and the activity you are launching must be on the same task for the startActivityForResult() to work properly that means that you can't call startActivityForResult() to an activity using the Intent.FLAG_ACTIVITY_NEW_TASK.
why singleInstance isn't working?
Because a "singleInstance" activity stands alone as the only activity in its task. If it starts another activity, that activity will be launched into a different task regardless of its launch mode — as if FLAG_ACTIVITY_NEW_TASK was in the intent. In all other respects, the "singleInstance" mode is identical to "singleTask".
Why singleTask is working?
Because singleTask doesn't require the creation of a new task for the new activities being launched.
I quoted from this answer to explain the difference between the two launch modes.
Have you declared the setResult(int resultCode, Intent data) in your Login activity.
Android Documentation says
"Note that this method should only be used with Intent protocols that are defined to return a result." If it is not or some other conditions matches as in the documentation of Activity.startActivityForResult(), it will not run in your task and thus you will immediately receive a cancel result."

Categories

Resources