StartActivity very slow if called while launcher is active? [duplicate] - java

I'm experiencing the problem described in this Android issue:
http://code.google.com/p/android/issues/detail?id=4536
Simply put, after pressing the HOME button, android prevents services and broadcast-receivers from calling startActivity for 5 seconds.
I've also noticed that (well, theoretically), having the following permission :
"android.permission.STOP_APP_SWITCHES"
allows you to call resumeAppSwitches (which is defined in ActivityManagerService).
Looking at the latest version of ActivityManagerService, this code is removed.
The question: How to launch an activity using startActivity without this 5 second delay?

Here is a solution I found.
Put your intent that you want to start immediately in a PendingIntent, then call the send() Method on it.
So instead of this
Intent intent = new Intent(context, A.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
just do this
Intent intent = new Intent(context, A.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent =
PendingIntent.getActivity(context, 0, intent, 0);
try {
pendingIntent.send();
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}

I don't think there is a way to do it with the current APIs. I think that is how they intended it to work so that an app cannot force itself back open when the user exits with a home key press. You could add the home/ launcher intent to the filter for whatever activity it is you are trying to start. Then the user would have the choice to basically treat that app as though it is a homescreen. Then it would get launched with no delay at all whenever the user presses the home button(They'd have to select it from the list that will pop up asking which app they want to use to complete this action, but they could check always use this app to take this step away in the future.)

I am using AlarmManager to start Activity immediatly from Service.
Activity starts without delay, even if you have pressed home button before.
Tested on Android 5.0.1 (Galaxy Alpha).
Don't work at 6.0.1 (Nexus 7 2013) :-(
Don't work at 4.1.2 (Galaxy S II) :-(
Don't work at 4.3 (ASUS MeMO Pad FHD 10 ME302C) :-(
#TargetApi(Build.VERSION_CODES.KITKAT)
private void startActivity() {
Intent intent = new Intent(this, Main.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
long now = Calendar.getInstance().getTimeInMillis();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
alarmManager.setExact(AlarmManager.RTC_WAKEUP, now, pendingIntent);
else
alarmManager.set(AlarmManager.RTC_WAKEUP, now, pendingIntent);
}

I am intrigued by this "feature" also and how to avoid it. Reading the post: http://code.google.com/p/android/issues/detail?id=4536 (read the comment #10).
I quote the relevant part below:
Workarounds are:
1) Don't use an
activity, do everything from a
service.
2) Have some kind of
intermediate Home (WidgetLocker
HomeHelper, QuickDesk, PowerStrip,
etc). These do a startActivity
immediate to start the "real" Home and
this bypasses the 5 second rule. This
is a bad idea as Android prioritizes
keeping the system Home in memory but
not whatever secondary Home the
intermediate set. So it can lead to
Launcher reloads which is no fun. Plus
it's very confusing to users.
3) Root
can start activities during this
period.
Among those, I believe the best way to do it is to create a "Home Helper"-like activity. So, instead of starting a new activity, you would call this one instead. This is specially true, since you are creating a launcher app.
As I said in my previous comment to the question, I would contact the WidgetLocker developer about it. Alternatively, you can use APK Manager to see how he implemented it (he even encouraged the use of the APK Manager to create different mods to his app, the link to the xda-developers thread is in the comment above)

Related

Initiliazing ParseLoginUI?

Where does one actually place the code to launch the ParseLoginUI activity?
ParseLoginBuilder builder = new ParseLoginBuilder(MainActivity.this);
startActivityForResult(builder.build(), 0);
Is it in the ParseLoginDispatchActivity? This was not made very clear at all within any of the official documentation:
https://github.com/ParsePlatform/ParseUI-Android
https://www.parse.com/docs/android/guide#user-interface
I'm importing ParseLoginUI into my existing app. What do I once I've installed everything, updated my manifests, my build.gradle and now want to actually launch the Login activity once my app launches?
Do I put something in my manifest to indicate that the ParseLoginActivity should launch first? That doesn't seem to work as an Activity from my main application is required to launch as the initial intent. I'm a little lost here... Any thoughts?
Well I did find one solution, albeit a trivial one:
Intent loginIntent = new Intent(MainActivity.this, ParseLoginActivity.class); startActivity(loginIntent);
I launched the above Intent with an options menu item, but you could do it with a button or whatever else suits your needs.
If you're importing ParseLoginUI into an existing app, it appears you can just launch ParseLoginActivity with a simple Intent. I wish they mentioned this on their integration tutorial. Seems like the most straightforward way to get it running.
This solution definitely launches the Activity you want, but it doesn't check for whether the user is logged in or not and hence doesn't redirect you to the appropriate pages in your log-in flow (which I believe has more to do with your Manifest). It does, however, allow you to successfully register a user and log in with Parse, which is a great start.
A better solution would be to add the following to the onCreate method in the Activity that launches when your app launches. So if when your app launches you land on FirstActivity, the following will check to see if you are logged in. If you are not, you will be sent the login screen, and if you are logged in you will be sent to the second Activity, which is presumably where your users will want to be when they open your app.
ParseUser currentUser = ParseUser.getCurrentUser();
if (currentUser != null) {
Intent launchMainActivity = new Intent(this, SecondActivity.class);
startActivity(launchMainActivity );
} else {
ParseLoginBuilder builder = new ParseLoginBuilder(FirstActivity.this);
startActivityForResult(builder.build(), 0);
}

Get Chosen App from Intent.createChooser

I am trying to capture the result of Intent.createChooser to know which app a user selected for sharing.
I know there have been a lot of posts related to this:
How to know which application the user chose when using an intent chooser?
https://stackoverflow.com/questions/6137592/how-to-know-the-action-choosed-in-a-intent-createchooser?rq=1
How to get the user selection from startActivityForResult(Intent.createChooser(fileIntent, "Open file using..."), APP_PICKED);?
Capturing and intercepting ACTION_SEND intents on Android
but these posts are somewhat old, and I am hoping that there might be some new developments.
I am trying to implement a share action without having it be present in the menu. The closest solution to what I want is provided by ClickClickClack who suggest implementing a custom app chooser, but that seems heavy handed. Plus, it seems like there might be some Android hooks to get the chosen app, like the ActivityChooserModel.OnChooseActivityListener.
I have the following code in my MainActivity, but the onShareTargetSelected method is never getting called.
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, shareMessage());
sendIntent.setType("text/plain");
Intent intent = Intent.createChooser(sendIntent, getResources().getText(R.string.share_prompt));
ShareActionProvider sap = new ShareActionProvider(this);
sap.setShareIntent(sendIntent);
sap.setOnShareTargetSelectedListener(new ShareActionProvider.OnShareTargetSelectedListener() {
#Override
public boolean onShareTargetSelected(ShareActionProvider source, Intent intent) {
System.out.println("Success!!");
return false;
}
});
startActivityForResult(intent, 1);
As of API level 22 it is now actually possible. In Android 5.1 a method (createChooser (Intent target, CharSequence title, IntentSender sender)) was added that allows for receiving the results of the user's choice. When you provide an IntentSender to createChooser, the sender will be notified by the chooser dialog with the ComponentName chosen by the user. It will be supplied in the extra named EXTRA_CHOSEN_COMPONENT int the IntentSender that is notified.
I am trying to capture the result of Intent.createChooser to know which app a user selected for sharing.
That is not possible.
Other "choosing" solutions, like ShareActionProvider, may offer more. I have not examined the Intent handed to onShareTargetSelected() to see if it contains the ComponentName of the chosen target, though the docs suggest that it should.
And, if for some reason it does not, you are welcome to try to fork ShareActionProvider to add the hooks you want.
The reason why createChooser() cannot be handled this way is simply because the "choosing" is being done by a separate process from yours.
I have the following code in my MainActivity, but the onShareTargetSelected method is never getting called.
ShareActionProvider goes in the action bar. You cannot just create an instance, call a couple of setters, and expect something to happen.

How to destroy an activity

In fact, I am new to Android App Development. In my application, I have a couple of activities and I have provided my users with an exit option menu to be able to leave the application. But there is a problem. When they hit the Exit button, they are able to leave the application but when they enter the application for the second time, the page that they left off the last time will be launched.
Here comes my code:
#Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
switch (item.getItemId()) {
case 0 :
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
Toast.makeText(this, "Goodbye Dear", Toast.LENGTH_LONG).show();
break;
Android Activity has two methods onPause and onDestroy where you can do the necessary cleanup.
http://developer.android.com/reference/android/app/Activity.html
Instead of using finish(), use System.exit(0);.
You have to override onPause and/or onDestroy methods inside your activity and delete your view within these methods.
The problem in your code is that Intent.FLAG_ACTIVITY_NEW_TASK doesn't remove your current Task. Read more about it here: Task and Back Stack | Android Developers.
Try using Intent.FLAG_ACTIVITY_CLEAR_TOP. From the documentation we can see that this gives the desired behavior.
If set, and the activity being launched is already running in the
current task, then instead of launching a new instance of that
activity, all of the other activities on top of it will be closed and
this Intent will be delivered to the (now on top) old activity as a
new Intent.

Android Notification setContentView

http://developer.android.com/guide/topics/ui/notifiers/notifications.html
Page says that, I have to use Intent
I can't use Intent because my code have only one Activity. I want to use notification to get back to app e.g. from DEVICE'S HOME SCREEN.
I have to use Intents when I want to execute something by clicking the notification. Is there a way to use setContentView after clicking the notification?
Without seeing what you have attempted so far, I am going to take a stab at this one.
I don't quite see why regardless of how many Activities you have in your app, you cannot declare / use an Intent. If you need to trigger your only Activity again, after a Notification has been clicked, and need to call the setContentView(R.layout.some_layout_xml); in the Activities onCreate() again, why not declare an Intent for your Notification like this:
Intent intent = new Intent(getApplicationContext(), YOUR_ACTIVITY.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(YOUR_ACTIVITY.this, 0, intent, 0);
This way, clicking on your Notification should do what you want it to do.
Again, this may or may not work for you considering that you haven't posted any code at all.
EDIT: Based on the comments by the OP, what I think the solution maybe:
Refer to this link on how to send data with an Intent for a Notification: How to send parameters from a notification-click to an activity?
Basically, since a Notification has been triggered, I am assuming that the user has already logged in. Now to skip the login part when they come back to the app after clicking a Notification, send some extras along with the Intent. Then, when the Activity starts, check for the value and using an if...else statement, decide which layout should be shown.
Again, and I cannot stress this enough, you should always show what you have done so far. That really helps finding a solution. Your actual requirement has no bearing on what you need done at all.
You can use an Intent to get back to the app, e.g. like this:
final Intent intent = new Intent(context, YourActivity.class);
final PendingIntent pendingIntent = PendingIntent.getActivity(
context, 0, intent, 0);
final Notification notification = new Notification(
R.drawable.logo, tickerText, System.currentTimeMillis());
notification.setLatestEventInfo(context, title, text, pendingIntent);
notificationManager.notify(NOTIFICATION_ID, notification);

Possible to skip track from an Android application?

I'm planning on doing a application for Android 2.1 that changes song every minute (through what I hope exists in Android, "next") for the application using the audio device atm.
So if I have Spotify running in background already, playing music, can I through my program change to the next track?
Let me know if I was unclear about anything.
Thanks in advance!
I know this is a bit old question, but it took me some time searching something other then what is mentioned here.
There is a workaround - broadcasting media button action. There is one catch - receiver can recognize if the broadcast was from system or from another app, so they can ignore the non-system broadcasts.
Intent i = new Intent(Intent.ACTION_MEDIA_BUTTON);
synchronized (this) {
i.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_NEXT));
sendOrderedBroadcast(i, null);
i.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_NEXT));
sendOrderedBroadcast(i, null);
}
There's no universal audio transport API for music applications, so you'd need to see if the music applications you're targeting publicly expose service bindings or intents. If not, you won't be able to do this.
Just posted a relevant answer here
Using the AudioManager's dispatchMediaKeyEvent() method with a defined KeyEvent worked for me using the latest SDK.
The system music homescreen widget sends this intent for the built-in music player:
final ComponentName serviceName = new ComponentName(context,
MediaPlaybackService.class);
intent = new Intent(MediaPlaybackService.NEXT_ACTION);
intent.setComponent(serviceName);
pendingIntent = PendingIntent.getService(context,
0 /* no requestCode */, intent, 0 /* no flags */);
views.setOnClickPendingIntent(R.id.control_next, pendingIntent);
But it looks like this might take some hackery to implement outside packages in the music app itself because the MediaPlaybackService only accepts explicit Intents and isn't accessible from the outside. This thread seems to indicate it's possible with a bit of hackery, though.
But even then, as Roman said, not every music player will respect that Intent. You'll have to check with Spotify/Pandora/Last.fm themselves and see if they have any available intents to bind like that.
Looks that it's possible to use AudioManager to inject media keys.
Here is a snippet from another question
this.mAudioManager = (AudioManager) this.context.getSystemService(Context.AUDIO_SERVICE);
long eventtime = SystemClock.uptimeMillis();
KeyEvent downEvent = new KeyEvent(eventtime, eventtime, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_NEXT, 0);
mAudioManager.dispatchMediaKeyEvent(downEvent);
KeyEvent upEvent = new KeyEvent(eventtime, eventtime, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_NEXT, 0);
mAudioManager.dispatchMediaKeyEvent(upEvent);
The same way you can inject PlayPause button and some others.
I've tested it within a background service controlling Youtube and it worked for Android 6

Categories