We are using intents to switch from one of our apps to another on request, typically a button that is pressed. That way our apps present themselves as a functional unit to the user. We switch back by means of results we hand back.
Recently we implemented a switch that is not triggered by an immediate user interaction, but by an implicit event. When navigating inside one app we use an activity from another app as a drop-in-replacement for some feature in the first app. So the first app sends the user to the second app if it detects that second app is installed.
I realize we have an issue here: if that second app is buggy, crashes, then the first app is broken. Since it switches to the second back again and again without the user being able to alter that behavior. So apparently it makes sense to enable the first app to realize that the second app just crashed. So that it would not switch over next time, although that second app is indeed installed.
This does not work out of the box, since the first app never is informed about the crash of the second app. Instead that app crashes, the OS hint comes app ("Unfortunately XY just crshed") and that's it. The flow never returns to the first app, so there is no return value to evaluate or similar.
So my question is: how can I enable the first app to learn about the fact that the second app just crashed?
I thought about registering a global handler for uncaught exceptions in the second app. That handler would have to somehow signal to the first app that it is currently crashing. Either by sending a specific intent itself (sounds ugly) or by handing back a result (is that possible at all?).
Is there a common practice for this? What is it? What other options exist? Or is that question itself obsolete, since I only failed to see how this is meant to be done?
UPDATE:
Interesting enough I learend that this actually works if the crash in that second app is raised not upon startup but much later ... In that case indeed the first app is in foreground again and is indeed able to detect the crash: the result is "CANCELED" in that case, which is perfectly fine for me.
The the question is reduced to "early crashes" and how this can be handled. Or, the other way round: what is the difference between early and later crashes in that second app?
You technically can't detect that the app crashed, but you can keep something like a "failure counter" in your 1st app. Basically, every time you're about to launch app #2, save a variable in SharedPreferences, something like launchAttempts. You'll want to increment it every time right before you launch app #2, and then decrement it in your onActivityResult() when a result is returned. Then you can set a threshold for how many failed attempts would qualify for "too many", and stop launching app #2
Related
I've been asked to fix an android application which has issues after switching to a newer device. The switch was from android 7/8 to 11. The application opens up google maps for navigation but very often when returned to the app all data is lost, and clicking on anything will reset it back to the startup activity. It doesn't matter which apps it switches between the issue happens with all of them.
I've tried several things:
-Disabling all battery saving options on the device
-Using OnSaveInstanceState to retain the data
-Using a backgroundservice to retain the data
The issue only occurs on actual devices, using an emulator results in no errors.
When the debugger is attached to the application it loses its connection before returning to the app. It is possible to re-attach when returning to the app but it disconnects as soon as
anything is clicked.
And best of all there's literally no error message shown anywhere.
The following comment provided the solution I needed.
You would need to start a foreground service, complete with its Notification. Even that does not guarantee that your process will remain around.
– CommonsWare Sep 1 at 12:40
I have a bug in my app which I thought I knew how to resolve, but now I've thought more about what is happening I'm not sure I do know the fix.
My app receives an incoming intent from a calling third party app as a string. That string can be sent as a SEND intent or a VIEW intent.
All of that works fine unless the app is already running...
This is what is happening:
My app is not running (not listed in the running apps view)
Share is clicked in another (third party) app and my app is selected to receive the shared text (text1).
My app opens and the text is displayed (and processed) as expected.
The user switches back to the third party app and shares some different text (text2) and my app is selected to receive this new text.
My app opens, but the original text (text1) is still displayed.
At this point I thought that the bug was because I am reading the intent in onCreate() and then displaying and processing it. My thinking was that as the app is already running onCreate() is not being called when the app is shown the second time as we jump into the lifecycle at onResume().
However, if I continue the test as follows:
Without exiting my app the user switches back to the third party app again and again shares the same piece of second text (text2) with my app.
My app is displayed again but this time correctly shows and processes the second text.
How can this be, as the app is still running, surely onCreate() is still not going to be called!
I thought that the fix was going to be simply to move the getIntent() call into onResume() (or onStart() ?) But now I'm not sure about this. Is this right thing to do?
The core of the issue is the fact that your Activity is already on top of the activity stack when the second Intent is being fired.
The launch mode will matter here.
What you can do is, set the launchMode to singleTop, and then get the intent in onNewIntent() method.
That is a good way of handling such scenarios. Please read the link above and it will make things very clear.
I have an app for the SW2 that has a user option for whether the app should use low-power mode (LPM). Which works fine. The problem is that the SW API only calls my app's supportsLowPowerMode() registration method once, when it first starts up. Meaning that if the user later changes the setting in my app, it won't take effect until the whole shebang restarts.
I've tried a few tricks (like killing my app's process) to force a reload, but nothing's worked so far. My last resort is telling the user that s/he needs to reboot the phone before this takes effect, but that's pretty hokey. Is there a better way?
The solution is not to try to change your response to supportsLowPowerMode() at runtime, but to start and stop your extension instead.
Basically, if your extension can ever support LPM, then it should be returning a value of true in supportsLowPowerMode(). If the extension's LPM usage can change, you handle that by simply doing different things in the onActiveLowPowerModeChange() handler. Specifically, if the user has chosen NOT to use LPM, you want a handler that looks like this:
#Override
public void onActiveLowPowerModeChange(boolean lowPowerModeOn) {
super.onActiveLowPowerModeChange(lowPowerModeOn);
if (lowPowerModeOn) {
// User doesn't want to use LPM, so stop the app on the SW
stopRequest();
}
}
This has the effect of shutting down your extension when the device's backlight turns off, and it'll drop back to its default LPM watchface.
EDIT: I'm reopening this as I've found an unacceptable side effect to the above technique. If your app returns false to supportsLowPowerMode(), the SW2 will keep it running (but paused) in the background when the watch goes to sleep. This means that, when the user "wakes up" the SW2, your app will immediately reappear on screen. Calling stopRequest() (as in my code sample above) disrupts this behavior, requiring the user to reopen your app after awakening the SW2. I've not found any way around this; what's needed is a pauseRequest() instead, but the API doesn't have one.
As far as I know there is no way to change this at runtime. Let me double check with my team though just in case there is a way I don't know about.
I'm having a bit of an issue with interaction beween my app and other apps on my phone, but I'm starting to think that maybe it's working as designed? Anyway, here's the problem.
My App, call it App A is a photo-manipulation app. So a user goes in, plays around to make changes and then uses the Share button to pass it on (SEND Intent). The photo is sent to another app, chosen from the Share menu, call it App B. This stand-alone app has its own menus, completely different look and feel, etc. The user does his thing in this app for a bit, then hits the home button and goes his way to do something else.
Sometime later, he decides he wants to run my app again. He goes into the launcher, hits the icon for App A (my app), and up pops App B. Very confusing. If he happens to remember that last time he ran App A, he used the share button to get into App B, maybe he'll think to use the back button, to get back into App A. If he doesn't remember, all he knows is that he is trying to use App A, but Android is giving him App B.
(I have one app on my phone that takes over the back button for its own use so you more-or-less get stuck in App B with no way out. Ugh. You hit the icon for App A and always end up in App B)
Is there any solution to this, or is it working as designed? None of my onCreate, OnResume, onStart, etc. methods get called when this is second-open is occurring, so I can't trap it. And realistically, I can see the desire for this behavior when timelines are short - i.e. hit the home button, quickly use some other tool, and then go back to what you were doing. But with a timeline any longer than a minute or two, it gets very confusing.
Anybody else dealing with this problem? Is there a basic Android architectural issue here? Is the SEND intent being mis-used by being accepted by stand-alone apps instead of small utilities?
I think you may use Intent flag 'FLAG_ACTIVITY_NO_HISTORY'. It means starting intent never goes into activity stack.
I want to create a password-protected android application. The password should be asked when the task is started or when it comes to foreground.
The complex part is that my app can invoke device inbuilt camera application to capture photo/video, so I cannot control that activity.
So the behavior should be that whenever user starts/returns to my app then password should be asked except for the scenario when he is returning from camera that I sent him to in the first place.
I have thought following ideas, but none of them solve my problem completely, the best one I could think of is
1) Set a flag(flagA) while sending the user to camera, then in a service keep checking that the foreground app is our app. When the foreground app is not our app then set another flag(flagB) to indicate that our app has gone to background. When the app in foreground is our app again, then check if flagA is false (we didnt send him to camera) and flagB is true (app is coming from background). Then show the password screen
Problems
1) The polling interval must be small like 3-4 seconds, so that foreground is detected quickly. This drains the battery.
Thanks
A few ideas come to mind.
This is just a quick idea of mine so it might not be useful but considering you haven't told us what you've already tried / have in mind we don't have a lot to go with.
You'll need at least 2 Activities to handle the whole password idea.
Firstly, when you start the app, run Activity A. This will ask for the password (assuming the password is always the same). If the password is correct -> start an intent with Activity B.
If the password is incorrect, close the app.
In B, give the user the option to invoke the camera. After using the camera, return to Activity B.
It doesn't get much better with the amount of info you've given us, but it should give you an idea on how to start.
I have solved the problem partially
1) Have a static long variable to track the time between onPause() and onResume(). During normal app operation this time is under 1 second, but if app goes to background and comes to foreground then this duration is more than 1 second. So in onResume() if the duration is more than 1 second then show the Password screen.
2) To handle the camera problem I am having a flag to detect whether I opened the camera, if that flag is set then dont ask for password in onResume().
This solution is not fool proof but works in most scenarios.