there is many similar questions of how to resume activity from notification.
But my case is slightly different.
I have an activity which should only be launched from other activity, and never from home or applications list (let me call this "standalone" mode).
This activity is designed to upload file to the server and handles only one intent:
<activity android:name="FileUploader"
android:label="..."
>
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<data android:mimeType="*/*"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
So it could be launched, for example, from Gallery or Camera by means of "Share file". But not from applications list, since there is no way to pass file which should be uploaded and I don't want to add any UI for its selection: I want to keep application as simple as possible.
Here is small example: launch Gallery from Home screen, then select photo, tap share and select my FileUploader from the list.
Activities stack is: GalleryMain > Gal2 > ... > FileUploader
Now if I tap Home key - I return to Home screen and after tapping of Gallery again, I return directly to my FileUploader - this is exactly what I need, so this is ok for now.
Now keeping in mind than upload operation takes time I add service which is actually implements upload: FileUploaderService. I also want to add notification which shows current upload progress when (and only when) user leaves FileUploader activity by pressing Home key.
Tapping on this notification should return user to the FileUploader activity, resuming it in case if it is already launched, or starting it again if activity was finished by the system. !But! resuming or starting it in context of "parent" activity (Gallery in example above), since creating it in its own context leads to unwanted behavior: FileUploader icon becomes to show in Recent Apps list, allowing user to launch it in "standalone" mode.
So 2 cases:
1. Resume if already launched
2. Recreate if was destroyed by system.
I have found I way (maybe this is dirty hack, but it works) to implement first:
String className = FileUploader.class.getCanonicalName();
Intent notificationIntent = null;
mAM = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
Iterator<ActivityManager.RunningTaskInfo> iter = mAM.getRunningTasks(1000).iterator();
int pid = -1;
while (iter.hasNext()) {
ActivityManager.RunningTaskInfo ti = iter.next();
if (className.equals(ti.topActivity.getClassName())) {
pid = ti.id;
break;
}
}
if (pid != -1) {
Iterator<ActivityManager.RecentTaskInfo> iter2 = mAM.getRecentTasks(100, 0).iterator();
while (iter2.hasNext()) {
RecentTaskInfo ti = iter2.next();
if (pid == ti.id) {
notificationIntent = ti.baseIntent;
}
}
}
With this code I'm getting intent which was used to launch "parent" activity (eg. Gallery) and then set my notification with this intent. So it resumes Gallery just like from Home screen leading user directly to my activity, which is currently on top of the stack.
But what to do with 2: how to relaunch "parent" Activity (moreover, I even don't know how to find out which activity it was) and recreate its stack to state it was before activity was destroyed?
All "parent" activities are third-party, so I can't change their code.
Any suggestions?
Well, the only way I found to implement this is:
android:excludeFromRecents="true" android:launchMode="singleTask"
This is not 100% of what I want (eg. I can't return to "parent" application) after resume, but is better than nothing.
Related
My app has 2 activities: a main activity, and a detail activity. When I click a button on main it generates a push notification which when clicked brings up the details page.
The problem is, I do not believe I've configured anything to suggest that the activity should open with any different launch properties other than standard nor are their any flags set to suggest my activity open differently either. However when the notification is clicked and the new activity opens, when I click the back button I am taken back to the homescreen and no active tasks are available any longer.
I've noticed through experimentation that if I direct the intent triggered by the notification to go to back to the main activity instead of the detail activity it operates as I would expect. I can click the button on the first instance of main activity to fire the notification, click the notification to bring up the second instance of main activity, then press back to go back to the original instance of Main activity. It even has the correct state of details represented in the original (a text box populated with what I provided to trigger the first notification to say).
I've also found that if I direct the intent to fire the detail activity but set the affinity associated w/ the activity to something else that it SORT OF works by just creating a new task w/ that activity as the sole activity associated w/ the task. But this isn't what I want nor is it what I think should be happening anyway.
EDIT: I've added a button that takes me to the details activity through a standard intent. This works as intended. I've also added a button to the details activity that generates a notification to the Main Activity operating on the same logic as the button on Main that fires the notification to go to the Details Activity except with the intent class changed. Clicking this generates the notification, and pushes the main activity onto the already existing task stack as is standard.
So it seems like the issue is related to the Detail activity being targeted by the pending intent but I haven't figured out exactly what yet and following the details in the android website is of no help (though this is obvious as it's where I started from originally)
Code:
Generates the push notification (found on main activity)
fun generatePush2(view: View){
var generalTapIntent = Intent(this, ActivityDetail::class.java)
var pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, generalTapIntent, 0);
var notificationId = 0;
var builder = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("Instant Message")
.setContentText(txt_notification.text)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent) //sets the event to fire when notification is clicked
.setAutoCancel(true) //Removes notification when user taps it
with(NotificationManagerCompat.from(this)){
notify(notificationId, builder.build());
}
}
Android Manifest details (for proof)
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".ActivityDetail">
</activity>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
you can override the onBackPressed() like this:
public void onBackPressed()
{
this.startActivity(new Intent(DetailActivity.this,MainActivity.class));
return;
}
if you want the main activity to look different than default you can still pass some data through the intent and display it the way you want using :
Detail Activity
intent.putExtra(String key, Object data);
Main Activity
intent.getExtra(String key);
SO while not understanding the bug fully, I have found that the issue is related to two things. The AVD I was using and the activity name. On the original AVD I changed the activity name from ActivityDetail to ActivityDetail2 and this fixed the entire problem. I realized something was up w/ the activity when creating a third activity didn't have the issue. Unfortunately upon changing the name back (shift + f6 in Android studio) the problem began to occur again. Perplexed I then deleted the AVD (had to reinstall android studio to fix emulator:process failed for error code 0 error) and created a new one with a different phone type and different version of android. I was then able to run the application w/ the activity name "ActivityDetail" and the problem didn't occur.
I was also not able to re-create the issue on the original AVD. This tells me that something on the AVD was associated to that specific activity name, set somehow through an application call I might have made while following the guide on the official android notification page: https://developer.android.com/training/notify-user/build-notification
Unfortunately that's all I can say for certain. That and there's no need to overwrite the on back key action just to fix this issue.
Consider 3 activities:
(A) Login Activity (main launcher)
(B) Main Activity
(C) Specific Activity
After users logged in (A) they will access (B), and from, and ONLY from (B), they can access (C).
Now everytime the app is opened, it will first launch (A) and then (B).
I want to make a push notification where when clicked, it can access (C) but must start (B) first.
I am using Xamarin.Android + Appcenter Push Notification
I can get notifications both when my app is in foreground and background.
My problem is when my app is in the background, clicking at the received notifications in the status bar causes it to relaunch the app, starting from (A).
I need help with skipping (A) since user is already logged in, opens (B) and THEN opens (C)
Any ideas? Hoping this is not too confusing for you guys.
I have also tried setting launchMode=singleInstance to (A) and (B) but it still relaunch the app
You cannot change the launcher activity dynamically, but there are a few workarounds you can try:
Create a transparent activity and make it as the Launcher activity:
<activity
android:name=".ActivityLauncher"
android:theme="#android:style/Theme.Translucent.NoTitleBar.Fullscreen" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
And select the next activity in it's onCreate()
if ( logged() ) {
intent = new Intent(this,ActivityB.class);
} else {
intent = new Intent(this,ActivityA.class);
}
startActivity(intent);
finish();
You can achieve this send a activity name or some value to select your activity. Depending on the value that u are sending you can launch the activity and also add the activity in backstack whenever u go back fron notified activity the page in backstack will appear.
I do always like this, hope this will work for you.
I have an app that may run in the background. While it is running, it may bring a certain activity to the front. However, when the app brings the activity to the front, if the app is currently running at background, I do not want Android to bring the app itself to the front, just do it at background. The reason being is I do not want my app to interrupt what the customer is doing at the moment.
The following is my code that launches the activity.
Intent intentLogin = new Intent(getApplicationContext(), LoginActivity.class);
intentLogin.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK|Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intentLogin);
The issue with the above code is every time when it is called, it always bring my app to the front even the app is running at background, that is, annoy my customer.
My question is, is there a way that I can quietly bring the activity to the front? If my app is running in front, thats great --- the customer see the new activity straight away. If my app is running at background, I want to quietly bring the activity to the front when app is running in background, and next time when my customer resume the app, they will see the new activity I brought forward.
How can I do that?
Thank you very much!
如果我没理解错的话:
App background and usual player.
When user logged out , you can clear the user data saved in local and show a notification.And when the notification is clicked, start LoginActivity.
App background and unusual player.
When the notification shows ,the user do not click it instead of run the app.You can check the user data whether existing or not by onResume()in BaseActivity.If not ,start LoginActivity.
App foreground.
clear user data and force to start LoginActivity.
If you don't like the solution above(the best user experence I think),try this:
In onCreate() in the specific Activity ,use moveTaskToBack(true) to hide the intent.But remember to check whether the app is running background or foreground.you can refer to this.
If understand correctly, I suggest this:
User leaves your app, and when user come back to your app you want to show another activity instead of .MainActivity
define another activity as Main and with NoDisplay theme.
<activity
android:name=".MainActivity"
android:theme="#android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
then define your normal activities. In MainActivity class define a static variable. for example
public static boolean appRun = false;
onCreate() of SecondActivity change its value to true.
class SecondActivity extends Activity{
#Override
protected void onCreate(Bundle Saved){
super.onCreate(Saved);
MainActivity.appRun = true;
}
}
and onPause set this.finish(); to avoid not resuming to SecondActivity and force to start from MainActivity.
then onCreate() or onResume() of MainActivity check it s value and navigate to another activity.
if(appRun == true){
/*it means user went to SecondActivity and you want to display another*/
startActivity(new Intent(context, NewActivity.class));
}
else{
//this is first time so navigate to SecondActivity
}
Also If you can use Fragments this solution can work for that too.
I'm trying to make a Home Replacement app, but I'm running into a bunch of glitches. When the app launches for the first time, you go through several setup screens that allow you to configure basic settings. Once you are done with that, you get to the HomeScreen activity. In the AndroidManifest.xml I have included the following:
<activity android:name="HomeScreenMain"
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>
In the HomeScreen activity, I have included the following methods:
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (Intent.ACTION_MAIN.equals(intent.getAction())) {
getWindow().closeAllPanels();
}
}
public void onDestroy() {
super.onDestroy();
}
Also in the HomeScreen activity, I have a button that effectively exits the entire app. The associated code is:
public void exitApp(View view){
this.finish();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
So basically what I want is that when you get to the HomeScreen activity the first time, a prompt comes up telling you to select your default Home Screen (this doesn't happen unless I press the Home Button, I want this to happen as soon as the activity is launched). Once I do set this as my default Home Screen, it works, but only fundamentally. Pressing the home button brings me back to this activity (as it should), but when I tap the Exit button, I don't get returned to the stock Home Launcher, which is what I want.
First, your Manifest is ok,
but in your exitApp you want to finish the activity, right?
in your code you finish it and then start it again..
Pressing the home button brings me back to this activity (as it should),
but when I tap the Exit button, I don't get returned to the stock Launcher,
which is what I want.
if a user had installed another home replacment app (e.g. Go Launcher Ex)
and if the user had set Go Launcher as default before defaulting to your app,
you want to return to Go Launcher Ex, right?
I assume yes.
This is partially possible,
what you can do is prompting the user which home launcher to use
after exiting your launcher:
import android.content.pm.PackageManager;
public void exitApp()
{
//call this method to exit _CLEARLY_,
//and prompt the user which launcher to use next
//clear the default for your app (to show the prompt when exiting)
final PackageManager pm = getPackageManager();
pm.clearPackagePreferredActivities(getApplicationContext().getPackageName());
//exit _CLEARLY_
//calling finish(); would be ok also,
//but there would stay a 'zombie' in the dalvik cache
//and 'zombies' only use up your memory, so kill your entire app:
android.os.Process.killProcess(android.os.Process.myPid());
}
So basically what I want is that when you get to the HomeScreen
activity the first time, a prompt comes up telling you to select your
default Home Screen (this doesn't happen unless I press the Home
Button, I want this to happen as soon as the activity is launched).
then call this function in onCreate(),
it simulates a homebutton press by calling an intent with Intent.CATEGORY_HOME:
public void showPrompt()
{
Intent i = new Intent(Intent.ACTION_MAIN);
i.addCategory(Intent.CATEGORY_HOME);
startActivity(i);
}
Hope this is what you wanted
Here is my problem -
I copied my .apk file onto phone memory card and launch my application clicking on it and it allows me to install my application.I install my application.Finally,I got system installation pop up containing two options "Open" and "Done".When i click "Open" my application got launched.Up to this point everything is working without any problem.
Now in my application I click on a button and some download is taking place as a result(Showing progress dialog).Now I press a Home button,so my application goes to background.
Now I again launch my application by going inside Menu and clicking on my application icon.
Expected result - Still I Should see Progress Dialog for downloading.
Actual result - A new instance/session of my application is getting started.
So how to avoid this so that only one and one instance/session of my application should run.
#Palejandro, here you are. Put the code below into your main activity onCreate() method:
// Possible work around for market launches. See
// http://code.google.com/p/android/issues/detail?id=2373
// for more details. Essentially, the market launches the main activity
// on top of other activities.
// We never want this to happen. Instead, we check if we are the root
// and if not, we finish.
if (!isTaskRoot()) {
final Intent intent = getIntent();
final String intentAction = intent.getAction();
if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) {
Log.w(TAG, "Main Activity is not the root. Finishing Main Activity instead of launching.");
finish();
return;
}
}
I used this piece of code in my projects and it works fine!
I believe you need to put
<activity
android:launchMode="singleInstance"
</activity>
in the manifest file.
what do your OnPause, OnResume and OnCreate?
I will bet money you are not saving anything OnPause, and starting a new instance all the time via OnCreate.
You should read the notes on Activity Lifecycles.
If you haven't got this sorted yet, I would say your app is actually being killed when home is pressed, or perhaps you have a bug that doesn't latch onto whatever object is keeping state.
// put below code in your launcher activity before call super and setcontentview()
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
// get the info from the currently running task
List< ActivityManager.RunningTaskInfo > taskInfo = am.getRunningTasks(10);
boolean alreadyTask=false;
for(ActivityManager.RunningTaskInfo info : taskInfo){
ComponentName componentInfo = info.topActivity;
String value= componentInfo.getPackageName();
if(value.contains(getPackageName()) && !info.topActivity.getClassName().contains(getPackageName()+".LauncherActivity")){
alreadyTask=true;
Log.i(TAG, "second instance found!!!");
break;
}
}
if(alreadyTask){
finish();
}
I don't have a solution but the problem is that the intent used to start the app is different when you open it directly from install compared to opening it from your home screen. Since it will get started by two different intents it will open a new instance the second time round.
A quick work around is to avoid pressing "Open" when you have installed the application. Press "Done" and then find the application yourself.
See: http://code.google.com/p/android/issues/detail?id=2373