Back and volume buttons not working when app is resumed in certain conditions - android-activity

I'm starting a new activity to display a leaderboard in my Android App.
When the leaderboard activity is displayed, and the user presses the home key and then resumes the app (so the leaderboard activity comes up again), and then navigates back to the main activity by pressing the back key, the back key and volume keys stop working).
I've attempted to override onBackPressed in my activity class. I can confirm that when this problem occurs, onBackPressed is not called (When back / volume is working, pressing back does trigger onBackPressed).
Normally I get a message in LogCat when I press the back key 'Unimplemented WebView method onKeyDown called from android.webkit.WebView,onKeyDown(WebView.java.2178)' - Again I can confirm that this message doesn't come up when the back key isn't working
Armed with the above information, I can only assume that it's something to do with the View not having focus or something along those lines. I would point out that touch input on the screen itself does work. It just seems to be the back and volume keys/buttons that have no effect.
It's an openGL ES 2.0 app so in my onPause() I'm calling view.onPause(); and in onResume, I'm calling view.onResume();
I really have no idea what's going on and I've been on this for 3 days straight so if anyone can point me in the right direction, that would be great.
If the user comes out of the leaderboard and back into the main app before they press the home key, then everything is OK, it's just if the home key is pressed while the leaderboard activity is displayed as described above.
So when I'm at the point where the back / volume keys aren't working, if I click my scores button and fire up the leaderboard activity again, they work. On returning to my activity, they stop working again.
Not sure if this is relevant at all but the following shows how I'm starting my leaderboard activity:
if (scoresButtonPresses){
displayLeaderBoard();
}
void displayLeaderBoard(){
//Display the leaderboard if already signed in
if (checkSignedIn()){
startActivityForResult(Games.Leaderboards.getLeaderboardIntent(getApiClient(), leaderboardID), 1);
}
//if not already connected, then set flag and connect to play services before displaying leaderbaord
else{
signInAction=SHOW_LEADERBOARD;
getGameHelper().beginUserInitiatedSignIn();
}
}
#Override
public void onSignInSucceeded() {
//If the flag is set, then display the leaderboard
if (signInAction==DISPLAY_LEADERBOARD){
startActivityForResult(Games.Leaderboards.getLeaderboardIntent(getApiClient(), leaderboardID), 1);
}
//Otherwise, reset the flag and take no action
else {signInAction=NO_ACTION;}
}
This is driving me crazy so any help or even a nudge in the right direction would be very much appreciated!!
Edit
After much testing I have learned a couple of things:
If I remove the view.onPause() && view.onResume() the problem seems to go away. So this appears to be something to do with the way key events are captured by the view. Pausing and resuming seems to mess something up.
I have also tried removing the view.onPause() and view.onResume() as above, but instead, putting in View.setVisibility(View.GONE); and then making it visible again in public void onWindowFocusChanged(boolean hasFocus). Again, I get the same problem. Interestingly, when I open the leaderboard, as expected the view's staus is 'gone' then hitting the home key and running the app again, it's set back to 'visible' - I don't understand this behaviour but I'll ask another question for that.
Lastly, and this I find really odd. If I put my app back to as I had it, then after hitting the home key and relaunching the app via Eclipse (and I can do this multiple times) the problem doesn't seem to occur. So in that respect, it appears to be something to do with touching the screen.
Edit
It appears as though this isn't limited to my app. I've tested this on a couple of other apps from the Play store and get the same result.
One app clearly uses a single activity model like mine and the back and volume stop working throughout the app.
The other may use a different activity for it menu and game. When I test on this app, the back/volume breaks but if you start a game (therefore a second activity), the back key starts working again, even when you return to the first activity (recreated this activity?!)
So maybe I can get around this by ensuring the activity is recreated? I thought i was already but maybe I'm not getting something. Maybe it's something to do with the stack.........

I have no solution to your problem but some additional insights since I'm facing the same bug/problem with the leaderboards and the back button (and it's driving me crazy).
I'm using a single activity with fragments from which I login to Google Play, open Leaderboards and Achievements in the exact way you are doing it (startActivityForResult). Always when I come back from any Google Play Service activity my back button is broken. Sometimes it's even enough if I try to login to Google Play for it to break, meaning the login popup sometimes breaks the back button (but not always).
Some more insights which might help to solve the problem:
I'm not using OpenGL, so it's not related to this - I'm just using a single activity and fragments
It's also breaking for me when I just use the back button for going back from any Google Play Activity (I don't even need to go via the home key, just normal back is enough).
When the back button is broken the Activity is not receiving any Key Events (OnKeyDown, OnKeyUp, dispatchKeyEvent) are all not working, although using the activity and touching it fully works. My guess is that some view is catching the events...
Edit:
I also tried checking activity.getCurrentFocus() and activity.hasWindowFocus() as well as the parent and context classes of activity.getCurrentFocus() to see if there is any difference between when it's working and when it's not - and there isn't any difference...
Edit:
It seems that a simple call to View.requestFocus() is fixing the problem. For my single activity (using fragments implementation) I have now added:
#Override
public void onResume(){
super.onResume();
Fragment fragment = getFragmentManager().findFragmentById(R.id.fragmentContainer);
fragment.getView().requestFocus();
}
and this seems to be fixing the problem. Sill have to do long term checking if it's always fixed. In your OpenGL implementation I guess you will have to get the main view of the activity in some other way since you are not using fragments.

I know it's pretty old question, but for clarification: accepted answer is correct, but incomplete. Focus requesting should be done in onResume, onActivityResult, onSignInSucceeded and onSignInFailed. In first two you have! to call super. Some code:
private void requestFocusAfterGooglePlay(){
if(gimmeFocus==null)
return;
View currFocus=getCurrentFocus();
gimmeFocus.setFocusableInTouchMode(true);
gimmeFocus.setFocusable(true);
gimmeFocus.requestFocus();
gimmeFocus.setFocusableInTouchMode(false);
gimmeFocus.setFocusable(false);
if(currFocus!=null)
currFocus.requestFocus();
}
#Override
public void onSignInFailed() {
requestFocusAfterGooglePlay();
}
#Override
public void onSignInSucceeded() {
requestFocusAfterGooglePlay();
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
requestFocusAfterGooglePlay();
super.onActivityResult(requestCode, resultCode, data);
}
#Override
public void onResume(){
super.onResume();
requestFocusAfterGooglePlay();
}
in my case gimmeFocus is a dummy View created as below:
<View
android:id="#+id/gimme_focus"
android:layout_width="0px"
android:layout_height="0px"
android:focusable="false"
android:focusableInTouchMode="false"/>
I have dynamic layout so I can't never be sure that any view exists in my layout, so I've created this dummy View, it might be also added programmatically. You might choose your fixed View for gaining focus, but rembember that changing focus might change view's look (drawable state).

Related

How do you automatically focus on an inputfield when opening/activating the UI in Unity3d?

I am making a game where you must open or activate the UI with the space bar. Now, this works perfectly fine, but it is pretty annoying that every time you open the UI you must click on the inputfield to write in it. Is there any way around this? So is there a way to open or activate the UI without having to click on the field to be able to write in it?
I looked for YouTube videos and tried to find similar problems in other forums, but wasn't able to find a script, nor was I able to find some Unity settings to do so.
You could use e.g.
private class SelectOnEnable : MonoBehaviour
{
private void OnEnable()
{
EventSystem.current.SetSelectedGameObject(null);
EventSystem.current.SetSelectedGameObject(gameObject);
}
}
and attach it to whatever object that should become the selected one everytime it is enabled. See EventSystem.SetSelectedGameObject
Can't test it right now but it might still require the User to hit Enter in order to also actually set the Input field into edit mode. The upper line only sets it as selected UI element (similar to using TAB in a browser).
Otherwise I think you would go through
yourInputField.DeactivateInputField()
yourInputField.ActivateInputField();
to directly set it active. See InputField.ActivateInputField. Might have to do both in combination - again can't test right now ;)
Thank you very much, derHugo! Everything works like a charm now! You saved me a lot of time. Referring to your last comment, I used both of them, and it seems to work very well for me. Here is the code I used:
`private void OnEnable()
{
EventSystem.current.SetSelectedGameObject(gameObject);
GameManager.GetComponent().inputFieldInMainUi.ActivateInputField();
EventSystem.current.SetSelectedGameObject(null);
GameManager.GetComponent<InputFieldComparision>().inputFieldInMainUi.DeactivateInputField();
}`

GWT Forward Button

I'm having an issue handling the forward button.
Basically, when a user is on a page and has made changes without saving then presses the backwards or forwards button they are presented with a prompt and two options: Leave or Stay.
I have implemented the backwards button fine, and choosing to stay on the page works well using History.newItem(currentToken) - the back button is still clickable.
However with the forwards button, if I use History.newItem(currentToken), it brings this to the front of the history stack and the forward button can no longer be clicked.
History.replaceItem(currentToken) causes the same issue.
How do I handle the cancelling of a forwards action so that I stay on my current page, but the forwards button is still enabled?
#Override
public void onValueChange(ValueChangeEvent<String> event) {
logger.info("back button pressed: " + event.getValue());
String evenVal = event.getValue();
String token = History.getToken();
AbstractPresenter presenter = sessionKiosk.getCurrentlyShowingPresenter();
if (presenter instanceof NSRCommonWorksheetPresenter && sessionKiosk.isDirty()) {
((NSRCommonWorksheetPresenter)presenter).setHistoryToken(event.getValue());
((NSRCommonWorksheetPresenter)presenter).showUnsavedChangesLeavingPageDialog();
}
else {
handleHistoryEvent(event.getValue());
}
}
The dialog is shown and when I click on stay on page the following is called.
public void stayOnCurrentPage() {
if (eventMap.get(prevPage) != null) {
History.newItem(prevPage, false);
}
}
Update: Basically history.newItem(value) removes the use of the forward button. Is there another way to cancel the event? If I just do nothing, th page stays where i want but the url still updates
None of the 3 options in the else statement seem to work.
Thanks.
You can simply cancel the event without touching History or tokens.
UPDATE:
It appears from your code that you are not intercepting the event (back/forward button), but let it go through, get the new token, and then force a return to the previous state under certain circumstances.
I suggest using Activities and Places pattern where every "place" within your app has a corresponding "activity". Each activity in your app will implement GWT Activity interface which includes mayStop() method. This method is called before a user navigates away from a specific place in your app, giving you an opportunity to warn a user and cancel the navigation if necessary.
In general, this pattern offers a very robust support for the History mechanism, covering many use cases.
If you want to support History mechanism yourself, take a look at PlaceChangeRequestEvent - it allows you to warn a user who tries to navigate away from a place in your app.

GWT MVP without tracking History / ignoring back button

First off, before the flames start, I do know that trying to hinder the back button in the browser is a dumb idea. I would not try to do this, but my business partners are very insistent on it. We are porting an existing .exe to the web and their definition of this is 'run it in a browser' and not "make it a web site". So, fact that it's a bad idea (I agree), here's the question:
Is there a way to ignore or trick the GWT PlaceController / History manager mechanisms so that when the back button is pressed, it just stays on the same page?
I have used Window.addWindowClosingHandler to add a handler which will prompt the user if they want to leave the page and overriden the newItem() method of the defaultHistorian so that no history is tracked at all, but this isn't quite what the business people want.
What they'd like is to just ignore the back button so that nothing happens when it is clicked.
If anyone knows how to do this with GWT, I'd be very grateful.
And I"ve done a lot of google searching and haven't found anything exactly like this question. At least, not close enough to help.
I was able to get GWT to not accumulate any history so that when the user presses the BACK button, they cause an onWindowClosing event to happen and the Browser will prompt them if they want to stay or leave. This will accomplish the goal of not allowing the BACK button to take them back, but it's a bit Draconian. This code does that:
class TvHistorian extends PlaceHistoryHandler.DefaultHistorian
{
#Override
public void newItem(String token, boolean issueEvent) {
// don't do anything - this will prevent history from accumulating
}
}
final PlaceHistoryHandler historyHandler = new PlaceHistoryHandler(historyMapper, new TvHistorian());
I've tried a bunch of stuff including extending the PlaceController.goTo() to save the "lastNormalFlowPlace". Then, I tried to override the History.onValueChange to use this saved Place if it was different than what the event specified. But I think I missed some subtlety because that didn't work as expected.
With the above exception, my code looks almost exactly like what is documented here: http://www.gwtproject.org/doc/latest/DevGuideMvpActivitiesAndPlaces.html#Putting_it_all_together
I have already posted an answer in the same context.
Please have a look at below links:
promt user on backspace and browser backbutton in gwt
how can i get a prompt on url change
Disable back button in GWT
Try with any option:
History.addValueChangeHandler
WindowClosingHandler
Event.addNativePreviewHandler
$wnd.onbeforeunload
--EDIT--
Sample code: (It's working perfectly fine in Firefox, Chrome as well as IE9)
Note: add below code in the beginning of the onModuleLoad() method.
final String initToken = "Place";
History.addValueChangeHandler(new ValueChangeHandler<String>() {
#Override
public void onValueChange(ValueChangeEvent<String> event) {
String token = event.getValue();
if (!initToken.equalsIgnoreCase(token)) {
History.newItem(initToken);
}
}
});
// fire the initial history state.
History.fireCurrentHistoryState();
Note: add checks for other allowed history tokens.

Does Activity.onStop() get called when the tab closes or refreshes?

I thought I saw somewhere that GWT's Activity onStop() method allowed the app to react to page closing. However, in an experiment, this appears not to be the case. Is it supposed to work this way, or am I doing something wrong?
It's the method mayStop(), which is called when the Activity is closed or the page is closed. If you return a non null string it will be displayed in a window.confirm box where the user can dismiss leaving the activity or page.

Prevent cached iPhone webapp from reloading (scrolling to top)

I have an iPhone webapp that uses a cache manifest to work offline. I add the webapp to my home screen, use it (say scroll to a certain location on a page), then go back to homescreen.
When I open the app again, for a brief moment I see where I used to be (at that scrolled location on that page), but then the app "reloads" and I get scrolled to the top of the mainpage. Is there a way to prevent this "reloading"? This happens even in airplane mode (ie everything is working off the cache).
You're just seeing the default startup image, which is just a screenshot of the last place you were at. It's not "reloading"; the app wasn't loaded to begin with.
Search for "apple-touch-startup-image" to set a real loading image.
What I'm struggling with here is that the app actually seems to stay "in memory" longer if I use regular Safari as opposed to running in "apple-mobile-web-app-capable" mode. In the later case something as simple as pressing the home button, then task-switching back to the app causes a reload. Doing the same thing just in Safari often does not reload. So I'm worse off by using "apple-mobile-web-app-capable".
I don't believe there is a real 'reload' event. onload and onunload are all we get.
the onload handler starts up as if it is your first time coming to the page.
the onunload handler is the key to clearing out old content.
I like to provide alternate content for people who are coming back to my web app.
window.onunload=function(){
document.getElementsByTagName('body')[0].className+=' unloading'
}
And let the CSS do the dirty work to hide most of the body and show alternate content.
(this answer does not rely on jQuery or other frameworks)
// on load
window.scroll(0,0);
To ensure no old content is displayed while launching I use this in my page:
window.addEventListener('unload', function() { $('body').hide(); } );
Thus the last state of the page is empty and is what is shown to the user when the page is opened again.