You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@cordova.apache.org by "Alan Neveu (Created) (JIRA)" <ji...@apache.org> on 2012/04/16 21:38:17 UTC

[jira] [Created] (CB-520) WP7 Certification and the Back Button

WP7 Certification and the Back Button
-------------------------------------

                 Key: CB-520
                 URL: https://issues.apache.org/jira/browse/CB-520
             Project: Apache Callback
          Issue Type: Bug
          Components: WP7
    Affects Versions: 1.6.1
         Environment: VS.NET 2010 and WP7.1 emulator
            Reporter: Alan Neveu
            Assignee: Jesse MacFadyen
            Priority: Blocker


I tried submitting my PG 1.5 app to the Windows Marketplace and it was rejected due to WP7's requirements for the Back Button. I upgraded to PG 1.6.1 and I am inspecting how it works with the hardware back button.  It seems to work much better, but there are situations in my app in which I need to do a programmatic version of the hardware back button.  I am trying to use navigator.app.historyBack and it appears to work, but it does something slightly different than actually tapping the hardware back button does.  I have also tried using window.history.back and that works different yet.  I am using JQueryMobile 1.1.0 and so I wind up doing quite a lot of $.mobile.changePage calls, and because I use multiple .html files I also need to do some rel="external" links or window.location.href= calls.  Here is what currently happens with PG 1.6.1 in a simple Page1/Page2 JQueryMobile app when using the hardware back button versus using navigator.app.historyBack, vs. window.history.back:



SCENARIO #1 - using hardware BackButton only
Page 1 links to Page 2 using $.mobile.changePage("#Page2");
BackButton tap - goes back to Page1 but page is requested again and reloaded from scratch which is slow and the user loses any form data they had entered.
BackButton tap - exits app (great!)


SCENARIO #2 - hardware BackButton and navigator.app.backHistory
Page 1 links to Page 2 using $.mobile.changePage("#Page2");
navigator.app.backHistory(); goes back to Page1 served from cache, which is fast and form data is preserved. VERY NICE!!
BackButton tap: nothing happens
BackButton tap: Page1 is reloaded from scratch
BackButton tap: exits app


SCENARIO #3 - hardware backButton and window.history.back
Page 1 links to Page 2 using $.mobile.changePage("#Page2");
window.history.back(); goes back to Page1 served from cache (NICE!)
BackButton tap: Page1 is reloaded from scratch (DOH!)
BackButton tap: exits app


My recommendations:

1) Using the hardware backbutton should serve the history from cache rather than re-loading the page.

2) navigator.app.backhistory should be modified so that it does exactly the same thing as the hardware back button. It would be great if there was an option of whether to serve the page from cache or to re-load it.

I mark this case as "blocker" because it is not possible to get a WP7 app into the Marketplace unless the back button works exactly the way they want it to. It seems that keeping the history as thin as possible is the best way so that the number of paths through an app are minimized.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira

        

[jira] [Updated] (CB-520) WP7 Certification and the Back Button

Posted by "Jesse MacFadyen (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/CB-520?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Jesse MacFadyen updated CB-520:
-------------------------------

    Priority: Critical  (was: Minor)
    
> WP7 Certification and the Back Button
> -------------------------------------
>
>                 Key: CB-520
>                 URL: https://issues.apache.org/jira/browse/CB-520
>             Project: Apache Callback
>          Issue Type: Bug
>          Components: WP7
>    Affects Versions: 1.6.1
>         Environment: VS.NET 2010 and WP7.1 emulator
>            Reporter: Alan Neveu
>            Assignee: Jesse MacFadyen
>            Priority: Critical
>             Fix For: 1.8.0
>
>
> I tried submitting my PG 1.5 app to the Windows Marketplace and it was rejected due to WP7's requirements for the Back Button. I upgraded to PG 1.6.1 and I am inspecting how it works with the hardware back button.  It seems to work much better, but my app is designed so that it has a soft back button in the app in various places, and on WP7 the user can always tap the hardware back button.  I am trying to use navigator.app.historyBack and it appears to work, but it does something slightly different than actually tapping the hardware back button does.  I have also tried using window.history.back and that works different yet.  I am using JQueryMobile 1.1.0 and so I wind up doing quite a lot of $.mobile.changePage calls to #Page id's, and because I use multiple .html files I also need to do some rel="external" links or window.location.href= calls.  I think my needs are similar or the same as those of other WP7 developers.  Here is what currently happens with PG 1.6.1 in a simple Page1/Page2 JQueryMobile app when using the hardware back button versus using navigator.app.historyBack, vs. window.history.back:
> SCENARIO #1 - using hardware BackButton only
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> BackButton tap - goes back to Page1 but page is requested again and reloaded from scratch which is slow and the user loses any form data they had entered.
> BackButton tap - exits app (great!)
> SCENARIO #2 - hardware BackButton and navigator.app.backHistory
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> navigator.app.backHistory(); goes back to Page1 served from cache, which is fast and form data is preserved.
> BackButton tap: nothing happens
> BackButton tap: Page1 is reloaded from scratch
> BackButton tap: exits app
> SCENARIO #3 - hardware backButton and window.history.back
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> window.history.back(); goes back to Page1 served from cache.
> BackButton tap: Page1 is reloaded from scratch (DOH!)
> BackButton tap: exits app
> My Observations:
> 1) The hardware back button does not use the cached page - it reloads/re-requests the page. This is kind of a drag but I think we have to just go with this because it is the behavior that the Marketplace testers will be expecting and validating.
> 2) window.history.back() is giving better results than navigator.app.backHistory, but still not the same as the hardware back button. I think apps will fail Marketplace certification if they use either of these approaches for soft back buttons.
> 3) When going back to an external page (as opposed to a JQueryMobile #pageID), window.history.back works but navigator.app.backHistory does not seem to do anything at all.  I say window.history.back "works" but it is still the same result as in Scenario #3 above, which is not good.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira

       

[jira] [Updated] (CB-520) WP7 Certification and the Back Button

Posted by "Alan Neveu (Updated) (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/CB-520?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Alan Neveu updated CB-520:
--------------------------

    Comment: was deleted

(was: I have spent time re-reading the certification requirements from Microsoft regarding the (holy) back button. I have also spent time modifying the source for the WP7CordovaClassLib C# project. I created an additional commandCallParams.Action called "softbackbutton". This allows the javascript layer to perform a window.history.back while playing nicely with the hardware back button stack that Cordova maintains.  It is called like this:

cordova.exec(null, null, "CoreEvents", "SoftBackButton", null);

Here is the modified code in CordovaView.xaml.cs that handles commandCallParams, beginning in line 405 of CordovaWP7 1.6.1:

            if (commandCallParams == null)
            {
                // ERROR
                Debug.WriteLine("ScriptNotify :: " + commandStr);
            }
            else if (commandCallParams.Service == "CoreEvents")
            {
                switch (commandCallParams.Action.ToLower())
                {
                    case "overridebackbutton":
                        string args = commandCallParams.Args;
                        this.OverrideBackButton = (args != null && args.Length > 0 && args.ToLower() == "true");
                        break;
                }
                if (commandCallParams.Action.ToLower() == "softbackbutton")
                {
                    try
                    {
                        CordovaBrowser.InvokeScript("eval", "window.history.back()");
                        history.Pop();
                    }
                    catch (Exception ex)
                    {
                        //do nothing, must have been called from first page, not our fault here.
                    }
                }
            }
            else
            {
                //Debug.WriteLine("ProcessCommand :: " + commandStr);
                this.nativeExecution.ProcessCommand(commandCallParams);
            }
)
    
> WP7 Certification and the Back Button
> -------------------------------------
>
>                 Key: CB-520
>                 URL: https://issues.apache.org/jira/browse/CB-520
>             Project: Apache Callback
>          Issue Type: Bug
>          Components: WP7
>    Affects Versions: 1.6.1
>         Environment: VS.NET 2010 and WP7.1 emulator
>            Reporter: Alan Neveu
>            Assignee: Jesse MacFadyen
>            Priority: Minor
>
> I tried submitting my PG 1.5 app to the Windows Marketplace and it was rejected due to WP7's requirements for the Back Button. I upgraded to PG 1.6.1 and I am inspecting how it works with the hardware back button.  It seems to work much better, but my app is designed so that it has a soft back button in the app in various places, and on WP7 the user can always tap the hardware back button.  I am trying to use navigator.app.historyBack and it appears to work, but it does something slightly different than actually tapping the hardware back button does.  I have also tried using window.history.back and that works different yet.  I am using JQueryMobile 1.1.0 and so I wind up doing quite a lot of $.mobile.changePage calls to #Page id's, and because I use multiple .html files I also need to do some rel="external" links or window.location.href= calls.  I think my needs are similar or the same as those of other WP7 developers.  Here is what currently happens with PG 1.6.1 in a simple Page1/Page2 JQueryMobile app when using the hardware back button versus using navigator.app.historyBack, vs. window.history.back:
> SCENARIO #1 - using hardware BackButton only
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> BackButton tap - goes back to Page1 but page is requested again and reloaded from scratch which is slow and the user loses any form data they had entered.
> BackButton tap - exits app (great!)
> SCENARIO #2 - hardware BackButton and navigator.app.backHistory
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> navigator.app.backHistory(); goes back to Page1 served from cache, which is fast and form data is preserved.
> BackButton tap: nothing happens
> BackButton tap: Page1 is reloaded from scratch
> BackButton tap: exits app
> SCENARIO #3 - hardware backButton and window.history.back
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> window.history.back(); goes back to Page1 served from cache.
> BackButton tap: Page1 is reloaded from scratch (DOH!)
> BackButton tap: exits app
> My Observations:
> 1) The hardware back button does not use the cached page - it reloads/re-requests the page. This is kind of a drag but I think we have to just go with this because it is the behavior that the Marketplace testers will be expecting and validating.
> 2) window.history.back() is giving better results than navigator.app.backHistory, but still not the same as the hardware back button. I think apps will fail Marketplace certification if they use either of these approaches for soft back buttons.
> 3) When going back to an external page (as opposed to a JQueryMobile #pageID), window.history.back works but navigator.app.backHistory does not seem to do anything at all.  I say window.history.back "works" but it is still the same result as in Scenario #3 above, which is not good.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira

       

[jira] [Updated] (CB-520) WP7 Certification and the Back Button

Posted by "Alan Neveu (Updated) (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/CB-520?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Alan Neveu updated CB-520:
--------------------------

    Comment: was deleted

(was: Upon further investigation, it seems that in Scenarios #2 and #3 the device does not know that we went back in the history, and so when you tap the hardware back button it actually reloads the page that is currently being shown. I could be wrong, but this is the way it appears from my view.)
    
> WP7 Certification and the Back Button
> -------------------------------------
>
>                 Key: CB-520
>                 URL: https://issues.apache.org/jira/browse/CB-520
>             Project: Apache Callback
>          Issue Type: Bug
>          Components: WP7
>    Affects Versions: 1.6.1
>         Environment: VS.NET 2010 and WP7.1 emulator
>            Reporter: Alan Neveu
>            Assignee: Jesse MacFadyen
>            Priority: Minor
>
> I tried submitting my PG 1.5 app to the Windows Marketplace and it was rejected due to WP7's requirements for the Back Button. I upgraded to PG 1.6.1 and I am inspecting how it works with the hardware back button.  It seems to work much better, but my app is designed so that it has a soft back button in the app in various places, and on WP7 the user can always tap the hardware back button.  I am trying to use navigator.app.historyBack and it appears to work, but it does something slightly different than actually tapping the hardware back button does.  I have also tried using window.history.back and that works different yet.  I am using JQueryMobile 1.1.0 and so I wind up doing quite a lot of $.mobile.changePage calls to #Page id's, and because I use multiple .html files I also need to do some rel="external" links or window.location.href= calls.  I think my needs are similar or the same as those of other WP7 developers.  Here is what currently happens with PG 1.6.1 in a simple Page1/Page2 JQueryMobile app when using the hardware back button versus using navigator.app.historyBack, vs. window.history.back:
> SCENARIO #1 - using hardware BackButton only
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> BackButton tap - goes back to Page1 but page is requested again and reloaded from scratch which is slow and the user loses any form data they had entered.
> BackButton tap - exits app (great!)
> SCENARIO #2 - hardware BackButton and navigator.app.backHistory
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> navigator.app.backHistory(); goes back to Page1 served from cache, which is fast and form data is preserved.
> BackButton tap: nothing happens
> BackButton tap: Page1 is reloaded from scratch
> BackButton tap: exits app
> SCENARIO #3 - hardware backButton and window.history.back
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> window.history.back(); goes back to Page1 served from cache.
> BackButton tap: Page1 is reloaded from scratch (DOH!)
> BackButton tap: exits app
> My Observations:
> 1) The hardware back button does not use the cached page - it reloads/re-requests the page. This is kind of a drag but I think we have to just go with this because it is the behavior that the Marketplace testers will be expecting and validating.
> 2) window.history.back() is giving better results than navigator.app.backHistory, but still not the same as the hardware back button. I think apps will fail Marketplace certification if they use either of these approaches for soft back buttons.
> 3) When going back to an external page (as opposed to a JQueryMobile #pageID), window.history.back works but navigator.app.backHistory does not seem to do anything at all.  I say window.history.back "works" but it is still the same result as in Scenario #3 above, which is not good.
> My recommendations:
> 2) Perhaps PhoneGap can enhance the WP7 implementation of navigator.app.backHistory so that it works the same as the hardware back button. It should go back a page and reload that page, and then pressing the hardware back button on Page 1 should exit the app.
> I mark this case as "blocker" priority because it is not possible to get a WP7 app certified for the Marketplace unless the back button works exactly the way they want it to. It seems that for WP7 we have to design our apps so as to keep the history as small as possible so that the number of paths through an app are minimized. We need soft "back" buttons in iOS and BlackBerry, and they are helpful for Android and WP7, and if we can get navigator.app.backHistory to behave (on WP7) identical to the hardware back button, I think we will have forever solved the WP7 back button issue.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira

       

[jira] [Commented] (CB-520) WP7 Certification and the Back Button

Posted by "Alan Neveu (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/CB-520?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13259906#comment-13259906 ] 

Alan Neveu commented on CB-520:
-------------------------------

Update... My app just passed Marketplace certification.  Here is a functional summary of what I needed that the 1.6.1 build did not provide:

1) Because I use JQueryMobile, I needed the Back Button to be able to know if the previous page in the history was just a hash tag navigation, and if so, just do a window.history.back.

2) There are some instances in which my app needed the page to then also be reloaded, and so I had to put a parameter on the second page to indicate this, which is &_wpBackReload=1 .

3) The WP7 Marketplace requirements are very strict when it comes to hitting Back on the first page of the app and that it must exit the app.  If you wind up deep in the history and the tester thinks he is stuck in your app and no amount of hitting the Back button will exit the app, your app will be failed. So the best thing is to be able to tell Cordova to clear the stack whenever your app navigates to the Home page. That can be done using the historystackclear action.

4) Sometimes my app needed to just do a simple historystackpop and remove whatever is the top item in the stack. It is hard to explain why, other than perhaps a bug in WP7's embeddable browser control.  So that is why I needed the action called historystackpop.

5) My standard Back buttons for other platforms that do not really go back but instead just reload the home page needed to become "soft" back buttons if the running platform is WP7. So that is why I needed the action called "softbackbutton". This way my navigation looks 100% identical across WP7, iOS, Android, and BlackBerry, but the implementation is slightly different in that WP7 calls the "softbackbutton" action whereas the others navigate (forward) to the home page. The if statement for that lives in my javascript.

I hope this helps, it's kind of a mess I know.  Thanks for listening!
                
> WP7 Certification and the Back Button
> -------------------------------------
>
>                 Key: CB-520
>                 URL: https://issues.apache.org/jira/browse/CB-520
>             Project: Apache Callback
>          Issue Type: Bug
>          Components: WP7
>    Affects Versions: 1.6.1
>         Environment: VS.NET 2010 and WP7.1 emulator
>            Reporter: Alan Neveu
>            Assignee: Jesse MacFadyen
>            Priority: Minor
>
> I tried submitting my PG 1.5 app to the Windows Marketplace and it was rejected due to WP7's requirements for the Back Button. I upgraded to PG 1.6.1 and I am inspecting how it works with the hardware back button.  It seems to work much better, but my app is designed so that it has a soft back button in the app in various places, and on WP7 the user can always tap the hardware back button.  I am trying to use navigator.app.historyBack and it appears to work, but it does something slightly different than actually tapping the hardware back button does.  I have also tried using window.history.back and that works different yet.  I am using JQueryMobile 1.1.0 and so I wind up doing quite a lot of $.mobile.changePage calls to #Page id's, and because I use multiple .html files I also need to do some rel="external" links or window.location.href= calls.  I think my needs are similar or the same as those of other WP7 developers.  Here is what currently happens with PG 1.6.1 in a simple Page1/Page2 JQueryMobile app when using the hardware back button versus using navigator.app.historyBack, vs. window.history.back:
> SCENARIO #1 - using hardware BackButton only
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> BackButton tap - goes back to Page1 but page is requested again and reloaded from scratch which is slow and the user loses any form data they had entered.
> BackButton tap - exits app (great!)
> SCENARIO #2 - hardware BackButton and navigator.app.backHistory
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> navigator.app.backHistory(); goes back to Page1 served from cache, which is fast and form data is preserved.
> BackButton tap: nothing happens
> BackButton tap: Page1 is reloaded from scratch
> BackButton tap: exits app
> SCENARIO #3 - hardware backButton and window.history.back
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> window.history.back(); goes back to Page1 served from cache.
> BackButton tap: Page1 is reloaded from scratch (DOH!)
> BackButton tap: exits app
> My Observations:
> 1) The hardware back button does not use the cached page - it reloads/re-requests the page. This is kind of a drag but I think we have to just go with this because it is the behavior that the Marketplace testers will be expecting and validating.
> 2) window.history.back() is giving better results than navigator.app.backHistory, but still not the same as the hardware back button. I think apps will fail Marketplace certification if they use either of these approaches for soft back buttons.
> 3) When going back to an external page (as opposed to a JQueryMobile #pageID), window.history.back works but navigator.app.backHistory does not seem to do anything at all.  I say window.history.back "works" but it is still the same result as in Scenario #3 above, which is not good.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira

       

[jira] [Closed] (CB-520) WP7 Certification and the Back Button

Posted by "Jesse MacFadyen (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/CB-520?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Jesse MacFadyen closed CB-520.
------------------------------

    
> WP7 Certification and the Back Button
> -------------------------------------
>
>                 Key: CB-520
>                 URL: https://issues.apache.org/jira/browse/CB-520
>             Project: Apache Cordova
>          Issue Type: Bug
>          Components: WP7
>    Affects Versions: 1.6.1
>         Environment: VS.NET 2010 and WP7.1 emulator
>            Reporter: Alan Neveu
>            Assignee: Jesse MacFadyen
>            Priority: Critical
>             Fix For: 1.8.0
>
>
> I tried submitting my PG 1.5 app to the Windows Marketplace and it was rejected due to WP7's requirements for the Back Button. I upgraded to PG 1.6.1 and I am inspecting how it works with the hardware back button.  It seems to work much better, but my app is designed so that it has a soft back button in the app in various places, and on WP7 the user can always tap the hardware back button.  I am trying to use navigator.app.historyBack and it appears to work, but it does something slightly different than actually tapping the hardware back button does.  I have also tried using window.history.back and that works different yet.  I am using JQueryMobile 1.1.0 and so I wind up doing quite a lot of $.mobile.changePage calls to #Page id's, and because I use multiple .html files I also need to do some rel="external" links or window.location.href= calls.  I think my needs are similar or the same as those of other WP7 developers.  Here is what currently happens with PG 1.6.1 in a simple Page1/Page2 JQueryMobile app when using the hardware back button versus using navigator.app.historyBack, vs. window.history.back:
> SCENARIO #1 - using hardware BackButton only
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> BackButton tap - goes back to Page1 but page is requested again and reloaded from scratch which is slow and the user loses any form data they had entered.
> BackButton tap - exits app (great!)
> SCENARIO #2 - hardware BackButton and navigator.app.backHistory
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> navigator.app.backHistory(); goes back to Page1 served from cache, which is fast and form data is preserved.
> BackButton tap: nothing happens
> BackButton tap: Page1 is reloaded from scratch
> BackButton tap: exits app
> SCENARIO #3 - hardware backButton and window.history.back
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> window.history.back(); goes back to Page1 served from cache.
> BackButton tap: Page1 is reloaded from scratch (DOH!)
> BackButton tap: exits app
> My Observations:
> 1) The hardware back button does not use the cached page - it reloads/re-requests the page. This is kind of a drag but I think we have to just go with this because it is the behavior that the Marketplace testers will be expecting and validating.
> 2) window.history.back() is giving better results than navigator.app.backHistory, but still not the same as the hardware back button. I think apps will fail Marketplace certification if they use either of these approaches for soft back buttons.
> 3) When going back to an external page (as opposed to a JQueryMobile #pageID), window.history.back works but navigator.app.backHistory does not seem to do anything at all.  I say window.history.back "works" but it is still the same result as in Scenario #3 above, which is not good.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira

       

[jira] [Updated] (CB-520) WP7 Certification and the Back Button

Posted by "Alan Neveu (Updated) (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/CB-520?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Alan Neveu updated CB-520:
--------------------------

    Description: 
I tried submitting my PG 1.5 app to the Windows Marketplace and it was rejected due to WP7's requirements for the Back Button. I upgraded to PG 1.6.1 and I am inspecting how it works with the hardware back button.  It seems to work much better, but my app is designed so that it has a soft back button in the app in various places, and on WP7 the user can always tap the hardware back button.  I am trying to use navigator.app.historyBack and it appears to work, but it does something slightly different than actually tapping the hardware back button does.  I have also tried using window.history.back and that works different yet.  I am using JQueryMobile 1.1.0 and so I wind up doing quite a lot of $.mobile.changePage calls to #Page id's, and because I use multiple .html files I also need to do some rel="external" links or window.location.href= calls.  I think my needs are similar or the same as those of other WP7 developers.  Here is what currently happens with PG 1.6.1 in a simple Page1/Page2 JQueryMobile app when using the hardware back button versus using navigator.app.historyBack, vs. window.history.back:



SCENARIO #1 - using hardware BackButton only
Page 1 links to Page 2 using $.mobile.changePage("#Page2");
BackButton tap - goes back to Page1 but page is requested again and reloaded from scratch which is slow and the user loses any form data they had entered.
BackButton tap - exits app (great!)


SCENARIO #2 - hardware BackButton and navigator.app.backHistory
Page 1 links to Page 2 using $.mobile.changePage("#Page2");
navigator.app.backHistory(); goes back to Page1 served from cache, which is fast and form data is preserved.
BackButton tap: nothing happens
BackButton tap: Page1 is reloaded from scratch
BackButton tap: exits app


SCENARIO #3 - hardware backButton and window.history.back
Page 1 links to Page 2 using $.mobile.changePage("#Page2");
window.history.back(); goes back to Page1 served from cache.
BackButton tap: Page1 is reloaded from scratch (DOH!)
BackButton tap: exits app


My Observations:

1) The hardware back button does not use the cached page - it reloads/re-requests the page. This is kind of a drag but I think we have to just go with this because it is the behavior that the Marketplace testers will be expecting and validating.

2) window.history.back() is giving better results than navigator.app.backHistory, but still not the same as the hardware back button. I think apps will fail Marketplace certification if they use either of these approaches for soft back buttons.

3) When going back to an external page (as opposed to a JQueryMobile #pageID), window.history.back works but navigator.app.backHistory does not seem to do anything at all.  I say window.history.back "works" but it is still the same result as in Scenario #3 above, which is not good.

  was:
I tried submitting my PG 1.5 app to the Windows Marketplace and it was rejected due to WP7's requirements for the Back Button. I upgraded to PG 1.6.1 and I am inspecting how it works with the hardware back button.  It seems to work much better, but my app is designed so that it has a soft back button in the app in various places, and on WP7 the user can always tap the hardware back button.  I am trying to use navigator.app.historyBack and it appears to work, but it does something slightly different than actually tapping the hardware back button does.  I have also tried using window.history.back and that works different yet.  I am using JQueryMobile 1.1.0 and so I wind up doing quite a lot of $.mobile.changePage calls to #Page id's, and because I use multiple .html files I also need to do some rel="external" links or window.location.href= calls.  I think my needs are similar or the same as those of other WP7 developers.  Here is what currently happens with PG 1.6.1 in a simple Page1/Page2 JQueryMobile app when using the hardware back button versus using navigator.app.historyBack, vs. window.history.back:



SCENARIO #1 - using hardware BackButton only
Page 1 links to Page 2 using $.mobile.changePage("#Page2");
BackButton tap - goes back to Page1 but page is requested again and reloaded from scratch which is slow and the user loses any form data they had entered.
BackButton tap - exits app (great!)


SCENARIO #2 - hardware BackButton and navigator.app.backHistory
Page 1 links to Page 2 using $.mobile.changePage("#Page2");
navigator.app.backHistory(); goes back to Page1 served from cache, which is fast and form data is preserved.
BackButton tap: nothing happens
BackButton tap: Page1 is reloaded from scratch
BackButton tap: exits app


SCENARIO #3 - hardware backButton and window.history.back
Page 1 links to Page 2 using $.mobile.changePage("#Page2");
window.history.back(); goes back to Page1 served from cache.
BackButton tap: Page1 is reloaded from scratch (DOH!)
BackButton tap: exits app


My Observations:

1) The hardware back button does not use the cached page - it reloads/re-requests the page. This is kind of a drag but I think we have to just go with this because it is the behavior that the Marketplace testers will be expecting and validating.

2) window.history.back() is giving better results than navigator.app.backHistory, but still not the same as the hardware back button. I think apps will fail Marketplace certification if they use either of these approaches for soft back buttons.

3) When going back to an external page (as opposed to a JQueryMobile #pageID), window.history.back works but navigator.app.backHistory does not seem to do anything at all.  I say window.history.back "works" but it is still the same result as in Scenario #3 above, which is not good.

My recommendations:

2) Perhaps PhoneGap can enhance the WP7 implementation of navigator.app.backHistory so that it works the same as the hardware back button. It should go back a page and reload that page, and then pressing the hardware back button on Page 1 should exit the app.

I mark this case as "blocker" priority because it is not possible to get a WP7 app certified for the Marketplace unless the back button works exactly the way they want it to. It seems that for WP7 we have to design our apps so as to keep the history as small as possible so that the number of paths through an app are minimized. We need soft "back" buttons in iOS and BlackBerry, and they are helpful for Android and WP7, and if we can get navigator.app.backHistory to behave (on WP7) identical to the hardware back button, I think we will have forever solved the WP7 back button issue.

    
> WP7 Certification and the Back Button
> -------------------------------------
>
>                 Key: CB-520
>                 URL: https://issues.apache.org/jira/browse/CB-520
>             Project: Apache Callback
>          Issue Type: Bug
>          Components: WP7
>    Affects Versions: 1.6.1
>         Environment: VS.NET 2010 and WP7.1 emulator
>            Reporter: Alan Neveu
>            Assignee: Jesse MacFadyen
>            Priority: Minor
>
> I tried submitting my PG 1.5 app to the Windows Marketplace and it was rejected due to WP7's requirements for the Back Button. I upgraded to PG 1.6.1 and I am inspecting how it works with the hardware back button.  It seems to work much better, but my app is designed so that it has a soft back button in the app in various places, and on WP7 the user can always tap the hardware back button.  I am trying to use navigator.app.historyBack and it appears to work, but it does something slightly different than actually tapping the hardware back button does.  I have also tried using window.history.back and that works different yet.  I am using JQueryMobile 1.1.0 and so I wind up doing quite a lot of $.mobile.changePage calls to #Page id's, and because I use multiple .html files I also need to do some rel="external" links or window.location.href= calls.  I think my needs are similar or the same as those of other WP7 developers.  Here is what currently happens with PG 1.6.1 in a simple Page1/Page2 JQueryMobile app when using the hardware back button versus using navigator.app.historyBack, vs. window.history.back:
> SCENARIO #1 - using hardware BackButton only
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> BackButton tap - goes back to Page1 but page is requested again and reloaded from scratch which is slow and the user loses any form data they had entered.
> BackButton tap - exits app (great!)
> SCENARIO #2 - hardware BackButton and navigator.app.backHistory
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> navigator.app.backHistory(); goes back to Page1 served from cache, which is fast and form data is preserved.
> BackButton tap: nothing happens
> BackButton tap: Page1 is reloaded from scratch
> BackButton tap: exits app
> SCENARIO #3 - hardware backButton and window.history.back
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> window.history.back(); goes back to Page1 served from cache.
> BackButton tap: Page1 is reloaded from scratch (DOH!)
> BackButton tap: exits app
> My Observations:
> 1) The hardware back button does not use the cached page - it reloads/re-requests the page. This is kind of a drag but I think we have to just go with this because it is the behavior that the Marketplace testers will be expecting and validating.
> 2) window.history.back() is giving better results than navigator.app.backHistory, but still not the same as the hardware back button. I think apps will fail Marketplace certification if they use either of these approaches for soft back buttons.
> 3) When going back to an external page (as opposed to a JQueryMobile #pageID), window.history.back works but navigator.app.backHistory does not seem to do anything at all.  I say window.history.back "works" but it is still the same result as in Scenario #3 above, which is not good.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira

       

[jira] [Commented] (CB-520) WP7 Certification and the Back Button

Posted by "Alan Neveu (Commented) (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/CB-520?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13255892#comment-13255892 ] 

Alan Neveu commented on CB-520:
-------------------------------

I have spent time re-reading the certification requirements from Microsoft regarding the (holy) back button. I have also spent time modifying the source for the WP7CordovaClassLib C# project. I created an additional commandCallParams.Service called "CustomHack" with an action of "historypop". This allows javascript to do a simple window.history.back() and then also call:

cordova.exec(null, null, "CustomHack", "HistoryPop", null);

Which essentially tells the history stack that we have gone back one page.  Then when you tap the hardware back button it only takes tapping it once, rather than twice (or even more times depending on how many times you did a window.history.back).

I know this is ugly, but here is my code that adds the commandCallParams.Service for "CustomHack"

            if (commandCallParams == null)
            {
                // ERROR
                Debug.WriteLine("ScriptNotify :: " + commandStr);
            }
            else if (commandCallParams.Service == "CoreEvents")
            {
                switch (commandCallParams.Action.ToLower())
                {
                    case "overridebackbutton":
                        string args = commandCallParams.Args;
                        this.OverrideBackButton = (args != null && args.Length > 0 && args.ToLower() == "true");
                        break;
                }
            }
            else if (commandCallParams.Service == "CustomHack")
            {
                if (commandCallParams.Action.ToLower() == "historypop")
                {
                    if (history.Count > 1)
                    {
                        history.Pop();
                    }
                }
            }
            else
            {
                //Debug.WriteLine("ProcessCommand :: " + commandStr);
                this.nativeExecution.ProcessCommand(commandCallParams);
            }


I say this is "ugly" because I have intentionally used "CustomHack" for the commandCallParams.Service string. Probably using commandCallParams is not the method that you would choose to implement such a feature as this.  So that is why I named it intentionally ugly so that you can see what I am recommending and implement as you see fit.  As long as we can call it easily from javascript, that is really all we need.  So, in javascript we wind up using this as follows:

cordova.exec(null, null, "CustomHack", "HistoryPop", null);
window.history.back();

If you want to pull the window.history.back() into the CordovaClass too, that would be a fine idea, so that using this would just be one line of code from javascript.

I played around with doing things like replicating the operative code in page_BackKeyPress, as follows:

history.Pop();
Uri next = history.Peek();
IsBackButtonPressed = true;
CordovaBrowser.Navigate(next);

and I even tried looping through page_BackKeyPress instead of copying/pasting those lines of code. But it seems that the hardware back button is also doing something *special* in native/external code even before the Silverlight project gets to do its magic and override it, and the result was just not quite right.  So I think the best thing is to just use the simple window.history.back and inform the history stack of our action.

Love to hear your thoughts!
                
> WP7 Certification and the Back Button
> -------------------------------------
>
>                 Key: CB-520
>                 URL: https://issues.apache.org/jira/browse/CB-520
>             Project: Apache Callback
>          Issue Type: Bug
>          Components: WP7
>    Affects Versions: 1.6.1
>         Environment: VS.NET 2010 and WP7.1 emulator
>            Reporter: Alan Neveu
>            Assignee: Jesse MacFadyen
>            Priority: Blocker
>
> I tried submitting my PG 1.5 app to the Windows Marketplace and it was rejected due to WP7's requirements for the Back Button. I upgraded to PG 1.6.1 and I am inspecting how it works with the hardware back button.  It seems to work much better, but my app is designed so that it has a soft back button in the app in various places, and on WP7 the user can always tap the hardware back button.  I am trying to use navigator.app.historyBack and it appears to work, but it does something slightly different than actually tapping the hardware back button does.  I have also tried using window.history.back and that works different yet.  I am using JQueryMobile 1.1.0 and so I wind up doing quite a lot of $.mobile.changePage calls to #Page id's, and because I use multiple .html files I also need to do some rel="external" links or window.location.href= calls.  I think my needs are similar or the same as those of other WP7 developers.  Here is what currently happens with PG 1.6.1 in a simple Page1/Page2 JQueryMobile app when using the hardware back button versus using navigator.app.historyBack, vs. window.history.back:
> SCENARIO #1 - using hardware BackButton only
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> BackButton tap - goes back to Page1 but page is requested again and reloaded from scratch which is slow and the user loses any form data they had entered.
> BackButton tap - exits app (great!)
> SCENARIO #2 - hardware BackButton and navigator.app.backHistory
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> navigator.app.backHistory(); goes back to Page1 served from cache, which is fast and form data is preserved.
> BackButton tap: nothing happens
> BackButton tap: Page1 is reloaded from scratch
> BackButton tap: exits app
> SCENARIO #3 - hardware backButton and window.history.back
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> window.history.back(); goes back to Page1 served from cache.
> BackButton tap: Page1 is reloaded from scratch (DOH!)
> BackButton tap: exits app
> My Observations:
> 1) The hardware back button does not use the cached page - it reloads/re-requests the page. This is kind of a drag but I think we have to just go with this because it is the behavior that the Marketplace testers will be expecting and validating.
> 2) window.history.back() is giving better results than navigator.app.backHistory, but still not the same as the hardware back button. I think apps will fail Marketplace certification if they use either of these approaches for soft back buttons.
> 3) When going back to an external page (as opposed to a JQueryMobile #pageID), window.history.back works but navigator.app.backHistory does not seem to do anything at all.  I say window.history.back "works" but it is still the same result as in Scenario #3 above, which is not good.
> My recommendations:
> 2) Perhaps PhoneGap can enhance the WP7 implementation of navigator.app.backHistory so that it works the same as the hardware back button. It should go back a page and reload that page, and then pressing the hardware back button on Page 1 should exit the app.
> I mark this case as "blocker" priority because it is not possible to get a WP7 app certified for the Marketplace unless the back button works exactly the way they want it to. It seems that for WP7 we have to design our apps so as to keep the history as small as possible so that the number of paths through an app are minimized. We need soft "back" buttons in iOS and BlackBerry, and they are helpful for Android and WP7, and if we can get navigator.app.backHistory to behave (on WP7) identical to the hardware back button, I think we will have forever solved the WP7 back button issue.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira

       

[jira] [Updated] (CB-520) WP7 Certification and the Back Button

Posted by "Alan Neveu (Updated) (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/CB-520?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Alan Neveu updated CB-520:
--------------------------

    Description: 
I tried submitting my PG 1.5 app to the Windows Marketplace and it was rejected due to WP7's requirements for the Back Button. I upgraded to PG 1.6.1 and I am inspecting how it works with the hardware back button.  It seems to work much better, but my app is designed so that it has a soft back button in the app in various places, and on WP7 the user can always tap the hardware back button.  I am trying to use navigator.app.historyBack and it appears to work, but it does something slightly different than actually tapping the hardware back button does.  I have also tried using window.history.back and that works different yet.  I am using JQueryMobile 1.1.0 and so I wind up doing quite a lot of $.mobile.changePage calls to #Page id's, and because I use multiple .html files I also need to do some rel="external" links or window.location.href= calls.  I think my needs are similar or the same as those of other WP7 developers.  Here is what currently happens with PG 1.6.1 in a simple Page1/Page2 JQueryMobile app when using the hardware back button versus using navigator.app.historyBack, vs. window.history.back:



SCENARIO #1 - using hardware BackButton only
Page 1 links to Page 2 using $.mobile.changePage("#Page2");
BackButton tap - goes back to Page1 but page is requested again and reloaded from scratch which is slow and the user loses any form data they had entered.
BackButton tap - exits app (great!)


SCENARIO #2 - hardware BackButton and navigator.app.backHistory
Page 1 links to Page 2 using $.mobile.changePage("#Page2");
navigator.app.backHistory(); goes back to Page1 served from cache, which is fast and form data is preserved.
BackButton tap: nothing happens
BackButton tap: Page1 is reloaded from scratch
BackButton tap: exits app


SCENARIO #3 - hardware backButton and window.history.back
Page 1 links to Page 2 using $.mobile.changePage("#Page2");
window.history.back(); goes back to Page1 served from cache.
BackButton tap: Page1 is reloaded from scratch (DOH!)
BackButton tap: exits app


My Observations:

1) The hardware back button does not use the cached page - it reloads/re-requests the page. This is kind of a drag but I think we have to just go with this because it is the behavior that the Marketplace testers will be expecting and validating.

2) window.history.back() is giving better results than navigator.app.backHistory, but still not the same as the hardware back button. I think apps will fail Marketplace certification if they use either of these approaches for soft back buttons.

3) When going back to an external page (as opposed to a JQueryMobile #pageID), window.history.back works but navigator.app.backHistory does not seem to do anything at all.  I say window.history.back "works" but it is still the same result as in Scenario #3 above, which is not good.

My recommendations:

2) Perhaps PhoneGap can enhance the WP7 implementation of navigator.app.backHistory so that it works the same as the hardware back button. It should go back a page and reload that page, and then pressing the hardware back button on Page 1 should exit the app.

I mark this case as "blocker" priority because it is not possible to get a WP7 app certified for the Marketplace unless the back button works exactly the way they want it to. It seems that for WP7 we have to design our apps so as to keep the history as small as possible so that the number of paths through an app are minimized. We need soft "back" buttons in iOS and BlackBerry, and they are helpful for Android and WP7, and if we can get navigator.app.backHistory to behave (on WP7) identical to the hardware back button, I think we will have forever solved the WP7 back button issue.

  was:
I tried submitting my PG 1.5 app to the Windows Marketplace and it was rejected due to WP7's requirements for the Back Button. I upgraded to PG 1.6.1 and I am inspecting how it works with the hardware back button.  It seems to work much better, but there are situations in my app in which I need to do a programmatic version of the hardware back button.  I am trying to use navigator.app.historyBack and it appears to work, but it does something slightly different than actually tapping the hardware back button does.  I have also tried using window.history.back and that works different yet.  I am using JQueryMobile 1.1.0 and so I wind up doing quite a lot of $.mobile.changePage calls, and because I use multiple .html files I also need to do some rel="external" links or window.location.href= calls.  Here is what currently happens with PG 1.6.1 in a simple Page1/Page2 JQueryMobile app when using the hardware back button versus using navigator.app.historyBack, vs. window.history.back:



SCENARIO #1 - using hardware BackButton only
Page 1 links to Page 2 using $.mobile.changePage("#Page2");
BackButton tap - goes back to Page1 but page is requested again and reloaded from scratch which is slow and the user loses any form data they had entered.
BackButton tap - exits app (great!)


SCENARIO #2 - hardware BackButton and navigator.app.backHistory
Page 1 links to Page 2 using $.mobile.changePage("#Page2");
navigator.app.backHistory(); goes back to Page1 served from cache, which is fast and form data is preserved. VERY NICE!!
BackButton tap: nothing happens
BackButton tap: Page1 is reloaded from scratch
BackButton tap: exits app


SCENARIO #3 - hardware backButton and window.history.back
Page 1 links to Page 2 using $.mobile.changePage("#Page2");
window.history.back(); goes back to Page1 served from cache (NICE!)
BackButton tap: Page1 is reloaded from scratch (DOH!)
BackButton tap: exits app


My recommendations:

1) Using the hardware backbutton should serve the history from cache rather than re-loading the page.

2) navigator.app.backhistory should be modified so that it does exactly the same thing as the hardware back button. It would be great if there was an option of whether to serve the page from cache or to re-load it.

I mark this case as "blocker" because it is not possible to get a WP7 app into the Marketplace unless the back button works exactly the way they want it to. It seems that keeping the history as thin as possible is the best way so that the number of paths through an app are minimized.

    
> WP7 Certification and the Back Button
> -------------------------------------
>
>                 Key: CB-520
>                 URL: https://issues.apache.org/jira/browse/CB-520
>             Project: Apache Callback
>          Issue Type: Bug
>          Components: WP7
>    Affects Versions: 1.6.1
>         Environment: VS.NET 2010 and WP7.1 emulator
>            Reporter: Alan Neveu
>            Assignee: Jesse MacFadyen
>            Priority: Blocker
>
> I tried submitting my PG 1.5 app to the Windows Marketplace and it was rejected due to WP7's requirements for the Back Button. I upgraded to PG 1.6.1 and I am inspecting how it works with the hardware back button.  It seems to work much better, but my app is designed so that it has a soft back button in the app in various places, and on WP7 the user can always tap the hardware back button.  I am trying to use navigator.app.historyBack and it appears to work, but it does something slightly different than actually tapping the hardware back button does.  I have also tried using window.history.back and that works different yet.  I am using JQueryMobile 1.1.0 and so I wind up doing quite a lot of $.mobile.changePage calls to #Page id's, and because I use multiple .html files I also need to do some rel="external" links or window.location.href= calls.  I think my needs are similar or the same as those of other WP7 developers.  Here is what currently happens with PG 1.6.1 in a simple Page1/Page2 JQueryMobile app when using the hardware back button versus using navigator.app.historyBack, vs. window.history.back:
> SCENARIO #1 - using hardware BackButton only
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> BackButton tap - goes back to Page1 but page is requested again and reloaded from scratch which is slow and the user loses any form data they had entered.
> BackButton tap - exits app (great!)
> SCENARIO #2 - hardware BackButton and navigator.app.backHistory
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> navigator.app.backHistory(); goes back to Page1 served from cache, which is fast and form data is preserved.
> BackButton tap: nothing happens
> BackButton tap: Page1 is reloaded from scratch
> BackButton tap: exits app
> SCENARIO #3 - hardware backButton and window.history.back
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> window.history.back(); goes back to Page1 served from cache.
> BackButton tap: Page1 is reloaded from scratch (DOH!)
> BackButton tap: exits app
> My Observations:
> 1) The hardware back button does not use the cached page - it reloads/re-requests the page. This is kind of a drag but I think we have to just go with this because it is the behavior that the Marketplace testers will be expecting and validating.
> 2) window.history.back() is giving better results than navigator.app.backHistory, but still not the same as the hardware back button. I think apps will fail Marketplace certification if they use either of these approaches for soft back buttons.
> 3) When going back to an external page (as opposed to a JQueryMobile #pageID), window.history.back works but navigator.app.backHistory does not seem to do anything at all.  I say window.history.back "works" but it is still the same result as in Scenario #3 above, which is not good.
> My recommendations:
> 2) Perhaps PhoneGap can enhance the WP7 implementation of navigator.app.backHistory so that it works the same as the hardware back button. It should go back a page and reload that page, and then pressing the hardware back button on Page 1 should exit the app.
> I mark this case as "blocker" priority because it is not possible to get a WP7 app certified for the Marketplace unless the back button works exactly the way they want it to. It seems that for WP7 we have to design our apps so as to keep the history as small as possible so that the number of paths through an app are minimized. We need soft "back" buttons in iOS and BlackBerry, and they are helpful for Android and WP7, and if we can get navigator.app.backHistory to behave (on WP7) identical to the hardware back button, I think we will have forever solved the WP7 back button issue.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira

       

[jira] [Commented] (CB-520) WP7 Certification and the Back Button

Posted by "Alan Neveu (Commented) (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/CB-520?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13255049#comment-13255049 ] 

Alan Neveu commented on CB-520:
-------------------------------

Upon further investigation, it seems that in Scenarios #2 and #3 the device does not know that we went back in the history, and so when you tap the hardware back button it actually reloads the page that is currently being shown. I could be wrong, but this is the way it appears from my view.
                
> WP7 Certification and the Back Button
> -------------------------------------
>
>                 Key: CB-520
>                 URL: https://issues.apache.org/jira/browse/CB-520
>             Project: Apache Callback
>          Issue Type: Bug
>          Components: WP7
>    Affects Versions: 1.6.1
>         Environment: VS.NET 2010 and WP7.1 emulator
>            Reporter: Alan Neveu
>            Assignee: Jesse MacFadyen
>            Priority: Blocker
>
> I tried submitting my PG 1.5 app to the Windows Marketplace and it was rejected due to WP7's requirements for the Back Button. I upgraded to PG 1.6.1 and I am inspecting how it works with the hardware back button.  It seems to work much better, but there are situations in my app in which I need to do a programmatic version of the hardware back button.  I am trying to use navigator.app.historyBack and it appears to work, but it does something slightly different than actually tapping the hardware back button does.  I have also tried using window.history.back and that works different yet.  I am using JQueryMobile 1.1.0 and so I wind up doing quite a lot of $.mobile.changePage calls, and because I use multiple .html files I also need to do some rel="external" links or window.location.href= calls.  Here is what currently happens with PG 1.6.1 in a simple Page1/Page2 JQueryMobile app when using the hardware back button versus using navigator.app.historyBack, vs. window.history.back:
> SCENARIO #1 - using hardware BackButton only
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> BackButton tap - goes back to Page1 but page is requested again and reloaded from scratch which is slow and the user loses any form data they had entered.
> BackButton tap - exits app (great!)
> SCENARIO #2 - hardware BackButton and navigator.app.backHistory
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> navigator.app.backHistory(); goes back to Page1 served from cache, which is fast and form data is preserved. VERY NICE!!
> BackButton tap: nothing happens
> BackButton tap: Page1 is reloaded from scratch
> BackButton tap: exits app
> SCENARIO #3 - hardware backButton and window.history.back
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> window.history.back(); goes back to Page1 served from cache (NICE!)
> BackButton tap: Page1 is reloaded from scratch (DOH!)
> BackButton tap: exits app
> My recommendations:
> 1) Using the hardware backbutton should serve the history from cache rather than re-loading the page.
> 2) navigator.app.backhistory should be modified so that it does exactly the same thing as the hardware back button. It would be great if there was an option of whether to serve the page from cache or to re-load it.
> I mark this case as "blocker" because it is not possible to get a WP7 app into the Marketplace unless the back button works exactly the way they want it to. It seems that keeping the history as thin as possible is the best way so that the number of paths through an app are minimized.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira

        

[jira] [Resolved] (CB-520) WP7 Certification and the Back Button

Posted by "Jesse MacFadyen (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/CB-520?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Jesse MacFadyen resolved CB-520.
--------------------------------

    Resolution: Fixed

Commit is here:
https://git-wip-us.apache.org/repos/asf?p=incubator-cordova-wp7.git;a=commit;h=2fc1089ae5936ecaf627fad48dbfee9a2d5f3e08

Backbutton presses are passed to js, window.history.back()
If the page changes as a result of the js call, then the event is cancelled.
When the history stack is empty, the page does not change as a result of history.back(), so the default implementation of exiting the app is performed.

OverrideBackButton is still available if you need greater control over history management.


                
> WP7 Certification and the Back Button
> -------------------------------------
>
>                 Key: CB-520
>                 URL: https://issues.apache.org/jira/browse/CB-520
>             Project: Apache Callback
>          Issue Type: Bug
>          Components: WP7
>    Affects Versions: 1.6.1
>         Environment: VS.NET 2010 and WP7.1 emulator
>            Reporter: Alan Neveu
>            Assignee: Jesse MacFadyen
>            Priority: Critical
>             Fix For: 1.8.0
>
>
> I tried submitting my PG 1.5 app to the Windows Marketplace and it was rejected due to WP7's requirements for the Back Button. I upgraded to PG 1.6.1 and I am inspecting how it works with the hardware back button.  It seems to work much better, but my app is designed so that it has a soft back button in the app in various places, and on WP7 the user can always tap the hardware back button.  I am trying to use navigator.app.historyBack and it appears to work, but it does something slightly different than actually tapping the hardware back button does.  I have also tried using window.history.back and that works different yet.  I am using JQueryMobile 1.1.0 and so I wind up doing quite a lot of $.mobile.changePage calls to #Page id's, and because I use multiple .html files I also need to do some rel="external" links or window.location.href= calls.  I think my needs are similar or the same as those of other WP7 developers.  Here is what currently happens with PG 1.6.1 in a simple Page1/Page2 JQueryMobile app when using the hardware back button versus using navigator.app.historyBack, vs. window.history.back:
> SCENARIO #1 - using hardware BackButton only
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> BackButton tap - goes back to Page1 but page is requested again and reloaded from scratch which is slow and the user loses any form data they had entered.
> BackButton tap - exits app (great!)
> SCENARIO #2 - hardware BackButton and navigator.app.backHistory
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> navigator.app.backHistory(); goes back to Page1 served from cache, which is fast and form data is preserved.
> BackButton tap: nothing happens
> BackButton tap: Page1 is reloaded from scratch
> BackButton tap: exits app
> SCENARIO #3 - hardware backButton and window.history.back
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> window.history.back(); goes back to Page1 served from cache.
> BackButton tap: Page1 is reloaded from scratch (DOH!)
> BackButton tap: exits app
> My Observations:
> 1) The hardware back button does not use the cached page - it reloads/re-requests the page. This is kind of a drag but I think we have to just go with this because it is the behavior that the Marketplace testers will be expecting and validating.
> 2) window.history.back() is giving better results than navigator.app.backHistory, but still not the same as the hardware back button. I think apps will fail Marketplace certification if they use either of these approaches for soft back buttons.
> 3) When going back to an external page (as opposed to a JQueryMobile #pageID), window.history.back works but navigator.app.backHistory does not seem to do anything at all.  I say window.history.back "works" but it is still the same result as in Scenario #3 above, which is not good.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira

       

[jira] [Commented] (CB-520) WP7 Certification and the Back Button

Posted by "Alan Neveu (Commented) (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/CB-520?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13256926#comment-13256926 ] 

Alan Neveu commented on CB-520:
-------------------------------

I wound up modifying CordovaView.xaml.cs in WP7CordovaClassLib.  It turns out WP7 apps really need to be able to have more control over the history stack in order to comply with Marketplace tech cert requirements.  Here is the source I wound up settling on for CordovaView.xaml.cs...  Note the enhancements to void GapBrowser_ScriptNotify, and also to void page_BackKeyPress.

/*  
	Licensed under the Apache License, Version 2.0 (the "License");
	you may not use this file except in compliance with the License.
	You may obtain a copy of the License at
	
	http://www.apache.org/licenses/LICENSE-2.0
	
	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using System.IO.IsolatedStorage;
using System.Windows.Resources;
using System.Windows.Interop;
using System.Runtime.Serialization.Json;
using System.IO;
using System.ComponentModel;
using System.Xml.Linq;
using WP7CordovaClassLib.Cordova.Commands;
using System.Diagnostics;
using System.Text;
using WP7CordovaClassLib.Cordova;
using System.Threading;
using Microsoft.Phone.Shell;



namespace WP7CordovaClassLib
{
    public partial class CordovaView : UserControl
    {
       
        /// <summary>
        /// Indicates whether web control has been loaded and no additional initialization is needed.
        /// Prevents data clearing during page transitions.
        /// </summary>
        private bool IsBrowserInitialized = false;
        
        /// <summary>
        /// Set when the user attaches a back button handler inside the WebBrowser
        /// </summary>
        private bool OverrideBackButton = false;

        /// <summary>
        /// Used for keeping track of our history
        /// </summary>
        private Stack<Uri> history = new Stack<Uri>();
        private bool IsBackButtonPressed = false;


        private static string AppRoot = "/app/";


        /// <summary>
        /// Handles native api calls
        /// </summary>
        private NativeExecution nativeExecution;

        protected DOMStorageHelper domStorageHelper;
        protected OrientationHelper orientationHelper;

        public System.Windows.Controls.Grid _LayoutRoot
        {
            get
            {
                return ((System.Windows.Controls.Grid)(this.FindName("LayoutRoot")));
            }
        }

        public WebBrowser Browser
        {
            get
            {
                return CordovaBrowser;
            }
        }

        /*
         * Setting StartPageUri only has an effect if called before the view is loaded.
         **/
        protected Uri _startPageUri = null;
        public Uri StartPageUri
        {
            get
            {
                if (_startPageUri == null)
                {
                    // default
                    return new Uri( AppRoot + "www/index.html", UriKind.Relative);                    
                }
                else
                {
                    return _startPageUri;
                }
            }
            set
            {
                if (!this.IsBrowserInitialized)
                {
                    _startPageUri = value;
                }
            }
        }

        public CordovaView()
        {
            
            InitializeComponent();

            if (DesignerProperties.IsInDesignTool)
            {
                return;
            }


            StartupMode mode = PhoneApplicationService.Current.StartupMode;

            if (mode == StartupMode.Launch)
            {
                PhoneApplicationService service = PhoneApplicationService.Current;
                service.Activated += new EventHandler<Microsoft.Phone.Shell.ActivatedEventArgs>(AppActivated);
                service.Launching += new EventHandler<LaunchingEventArgs>(AppLaunching);
                service.Deactivated += new EventHandler<DeactivatedEventArgs>(AppDeactivated);
                service.Closing += new EventHandler<ClosingEventArgs>(AppClosing);
            }
            else
            {

            }

            // initializes native execution logic
            this.nativeExecution = new NativeExecution(ref this.CordovaBrowser);
        }

        

        void AppClosing(object sender, ClosingEventArgs e)
        {
            Debug.WriteLine("AppClosing");
        }

        void AppDeactivated(object sender, DeactivatedEventArgs e)
        {
            Debug.WriteLine("AppDeactivated");

            try
            {
                CordovaBrowser.InvokeScript("CordovaCommandResult", new string[] { "pause" });
            }
            catch (Exception)
            {
                Debug.WriteLine("Pause event error");
            } 
        }

        void AppLaunching(object sender, LaunchingEventArgs e)
        {
            Debug.WriteLine("AppLaunching");
        }

        void AppActivated(object sender, Microsoft.Phone.Shell.ActivatedEventArgs e)
        {
            Debug.WriteLine("AppActivated");
            try
            {
                CordovaBrowser.InvokeScript("CordovaCommandResult", new string[] { "resume" });
            }
            catch (Exception)
            {
                Debug.WriteLine("Resume event error");
            }  
        }

        void GapBrowser_Loaded(object sender, RoutedEventArgs e)
        {
            if (DesignerProperties.IsInDesignTool)
            {
                return;
            }

            // prevents refreshing web control to initial state during pages transitions
            if (this.IsBrowserInitialized) return;

            this.domStorageHelper = new DOMStorageHelper(this.CordovaBrowser);

            try
            {

                // Before we possibly clean the ISO-Store, we need to grab our generated UUID, so we can rewrite it after.
                string deviceUUID = "";

                using (IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    try
                    {
                        IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream("DeviceID.txt", FileMode.Open, FileAccess.Read, appStorage);

                        using (StreamReader reader = new StreamReader(fileStream))
                        {
                            deviceUUID = reader.ReadLine();
                        }
                    }
                    catch (Exception /*ex*/)
                    {
                        deviceUUID = Guid.NewGuid().ToString();
                    }

                    Debug.WriteLine("Updating IsolatedStorage for APP:DeviceID :: " + deviceUUID);
                    IsolatedStorageFileStream file = new IsolatedStorageFileStream("DeviceID.txt", FileMode.Create, FileAccess.Write, appStorage);
                    using (StreamWriter writeFile = new StreamWriter(file))
                    {
                        writeFile.WriteLine(deviceUUID);
                        writeFile.Close();
                    }
   
                }

                StreamResourceInfo streamInfo = Application.GetResourceStream(new Uri("CordovaSourceDictionary.xml", UriKind.Relative));

                if (streamInfo != null)
                {
                    StreamReader sr = new StreamReader(streamInfo.Stream);
                    //This will Read Keys Collection for the xml file

                    XDocument document = XDocument.Parse(sr.ReadToEnd());

                    var files = from results in document.Descendants("FilePath")
                                 select new
                                 {
                                     path =  (string)results.Attribute("Value")
                                 };
                    StreamResourceInfo fileResourceStreamInfo;

                    using (IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication())
                    {

                        foreach (var file in files)
                        {
                            fileResourceStreamInfo = Application.GetResourceStream(new Uri(file.path, UriKind.Relative));

                            if (fileResourceStreamInfo != null)
                            {
                                using (BinaryReader br = new BinaryReader(fileResourceStreamInfo.Stream))
                                {
                                    byte[] data = br.ReadBytes((int)fileResourceStreamInfo.Stream.Length);

                                    string strBaseDir = AppRoot + file.path.Substring(0, file.path.LastIndexOf(System.IO.Path.DirectorySeparatorChar));

                                    if(!appStorage.DirectoryExists(strBaseDir))
                                    {
                                        //Debug.WriteLine("Creating Directory :: " + strBaseDir);
                                        appStorage.CreateDirectory(strBaseDir);
                                    }

                                    // This will truncate/overwrite an existing file, or 
                                    using (IsolatedStorageFileStream outFile = appStorage.OpenFile(AppRoot + file.path, FileMode.Create))
                                    {
                                        Debug.WriteLine("Writing data for " + AppRoot + file.path + " and length = " + data.Length);
                                        using (var writer = new BinaryWriter(outFile))
                                        {
                                            writer.Write(data);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                Debug.WriteLine("Failed to write file :: " + file.path + " did you forget to add it to the project?");
                            }
                        }
                    }
                }

                CordovaBrowser.Navigate(StartPageUri);
                IsBrowserInitialized = true;
                AttachHardwareButtonHandlers();
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception in GapBrowser_Loaded :: {0}", ex.Message);
            }
        }

        void AttachHardwareButtonHandlers()
        {
            PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
            if (frame != null)
            {
                PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
                 
                if (page != null)
                {
                    page.BackKeyPress += new EventHandler<CancelEventArgs>(page_BackKeyPress);

                    this.orientationHelper = new OrientationHelper(this.CordovaBrowser, page); 

                }
            }
        }

        void page_BackKeyPress(object sender, CancelEventArgs e)
        {
            if (OverrideBackButton)
            {
                try
                {
                    CordovaBrowser.InvokeScript("CordovaCommandResult", new string[] { "backbutton" });
                    e.Cancel = true;
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Exception while invoking backbutton into cordova view: " + ex.Message);
                }
            }
            else
            {
                if (history.Count > 1)
                {
                    Uri current = history.Peek();
                    history.Pop();
                    Uri next = history.Peek();
                    IsBackButtonPressed = true;
                    bool doHistory = false;
                    if (current.ToString().IndexOf("#") > 0 && next.ToString().IndexOf("#") > 0)
                    {
                        if (current.ToString().Substring(0, current.ToString().IndexOf("#")) == next.ToString().Substring(0, next.ToString().IndexOf("#")))
                        {
                            doHistory = true;
                        }
                    }
                    if (doHistory)
                    {
                        CordovaBrowser.InvokeScript("eval", "window.history.back()");
                        CordovaBrowser.InvokeScript("eval", "window.location.reload()");
                    }
                    else
                    {
                        CordovaBrowser.Navigate(next);
                    }
                    e.Cancel = true;
                }
            }
        }

        void GapBrowser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
        {
            this.CordovaBrowser.Opacity = 1;

            string nativeReady = "(function(){ cordova.require('cordova/channel').onNativeReady.fire()})();";

            try
            {
                CordovaBrowser.InvokeScript("execScript", new string[] { nativeReady });
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Error calling js to fire nativeReady event. Did you include cordova-x.x.x.js in your html script tag?");
            }
        }


        void GapBrowser_Navigating(object sender, NavigatingEventArgs e)
        {
            if (!IsBackButtonPressed)
            {
                history.Push(e.Uri);
            }
            else
            {
                IsBackButtonPressed = false;
            }

            Debug.WriteLine("GapBrowser_Navigating to :: " + e.Uri.ToString());
            // TODO: tell any running plugins to stop doing what they are doing.
            // TODO: check whitelist / blacklist
            // NOTE: Navigation can be cancelled by setting :        e.Cancel = true;


        }

        /*
         *  This method does the work of routing commands
         *  NotifyEventArgs.Value contains a string passed from JS 
         *  If the command already exists in our map, we will just attempt to call the method(action) specified, and pass the args along
         *  Otherwise, we create a new instance of the command, add it to the map, and call it ...
         *  This method may also receive JS error messages caught by window.onerror, in any case where the commandStr does not appear to be a valid command
         *  it is simply output to the debugger output, and the method returns.
         * 
         **/
        void GapBrowser_ScriptNotify(object sender, NotifyEventArgs e)
        {
            string commandStr = e.Value;

            if (commandStr.IndexOf("DOMStorage") == 0)
            {
                this.domStorageHelper.HandleStorageCommand(commandStr);
                return;
            }
            else if (commandStr.IndexOf("Orientation") == 0)
            {
                this.orientationHelper.HandleCommand(commandStr);
                return;
            }

            CordovaCommandCall commandCallParams = CordovaCommandCall.Parse(commandStr);

            if (commandCallParams == null)
            {
                // ERROR
                Debug.WriteLine("ScriptNotify :: " + commandStr);
            }
            else if (commandCallParams.Service == "CoreEvents")
            {
                switch (commandCallParams.Action.ToLower())
                {
                    case "overridebackbutton":
                        string args = commandCallParams.Args;
                        this.OverrideBackButton = (args != null && args.Length > 0 && args.ToLower() == "true");
                        break;
                }
                if (commandCallParams.Action.ToLower() == "softbackbutton")
                {
                    try
                    {
                        CordovaBrowser.InvokeScript("eval", "window.history.back()");
                        history.Pop();
                    }
                    catch (Exception ex)
                    {
                        //do nothing, must have been called from first page, not our fault here.
                    }
                }
                if (commandCallParams.Action.ToLower() == "historystackpop")
                {
                    try
                    {
                        history.Pop();
                    }
                    catch (Exception ex)
                    {
                        //do nothing, must have been called from first page, not our fault here.
                    }
                }
                if (commandCallParams.Action.ToLower() == "historystackclear")
                {
                    try
                    {
                        history = new Stack<Uri>();
                    }
                    catch (Exception ex)
                    {
                        //do nothing, must have been called from first page, not our fault here.
                    }
                }
            }
            else
            {
                //Debug.WriteLine("ProcessCommand :: " + commandStr);
                this.nativeExecution.ProcessCommand(commandCallParams);
            }
        }

        private void GapBrowser_Unloaded(object sender, RoutedEventArgs e)
        {

        }

        private void GapBrowser_NavigationFailed(object sender, System.Windows.Navigation.NavigationFailedEventArgs e)
        {
            Debug.WriteLine("GapBrowser_NavigationFailed :: " + e.Uri.ToString());
        }

        private void GapBrowser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
        {
            Debug.WriteLine("GapBrowser_Navigated :: " + e.Uri.ToString());
        }

       
    }
}

                
> WP7 Certification and the Back Button
> -------------------------------------
>
>                 Key: CB-520
>                 URL: https://issues.apache.org/jira/browse/CB-520
>             Project: Apache Callback
>          Issue Type: Bug
>          Components: WP7
>    Affects Versions: 1.6.1
>         Environment: VS.NET 2010 and WP7.1 emulator
>            Reporter: Alan Neveu
>            Assignee: Jesse MacFadyen
>            Priority: Minor
>
> I tried submitting my PG 1.5 app to the Windows Marketplace and it was rejected due to WP7's requirements for the Back Button. I upgraded to PG 1.6.1 and I am inspecting how it works with the hardware back button.  It seems to work much better, but my app is designed so that it has a soft back button in the app in various places, and on WP7 the user can always tap the hardware back button.  I am trying to use navigator.app.historyBack and it appears to work, but it does something slightly different than actually tapping the hardware back button does.  I have also tried using window.history.back and that works different yet.  I am using JQueryMobile 1.1.0 and so I wind up doing quite a lot of $.mobile.changePage calls to #Page id's, and because I use multiple .html files I also need to do some rel="external" links or window.location.href= calls.  I think my needs are similar or the same as those of other WP7 developers.  Here is what currently happens with PG 1.6.1 in a simple Page1/Page2 JQueryMobile app when using the hardware back button versus using navigator.app.historyBack, vs. window.history.back:
> SCENARIO #1 - using hardware BackButton only
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> BackButton tap - goes back to Page1 but page is requested again and reloaded from scratch which is slow and the user loses any form data they had entered.
> BackButton tap - exits app (great!)
> SCENARIO #2 - hardware BackButton and navigator.app.backHistory
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> navigator.app.backHistory(); goes back to Page1 served from cache, which is fast and form data is preserved.
> BackButton tap: nothing happens
> BackButton tap: Page1 is reloaded from scratch
> BackButton tap: exits app
> SCENARIO #3 - hardware backButton and window.history.back
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> window.history.back(); goes back to Page1 served from cache.
> BackButton tap: Page1 is reloaded from scratch (DOH!)
> BackButton tap: exits app
> My Observations:
> 1) The hardware back button does not use the cached page - it reloads/re-requests the page. This is kind of a drag but I think we have to just go with this because it is the behavior that the Marketplace testers will be expecting and validating.
> 2) window.history.back() is giving better results than navigator.app.backHistory, but still not the same as the hardware back button. I think apps will fail Marketplace certification if they use either of these approaches for soft back buttons.
> 3) When going back to an external page (as opposed to a JQueryMobile #pageID), window.history.back works but navigator.app.backHistory does not seem to do anything at all.  I say window.history.back "works" but it is still the same result as in Scenario #3 above, which is not good.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira

       

[jira] [Updated] (CB-520) WP7 Certification and the Back Button

Posted by "Alan Neveu (Updated) (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/CB-520?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Alan Neveu updated CB-520:
--------------------------

    Priority: Minor  (was: Blocker)
    
> WP7 Certification and the Back Button
> -------------------------------------
>
>                 Key: CB-520
>                 URL: https://issues.apache.org/jira/browse/CB-520
>             Project: Apache Callback
>          Issue Type: Bug
>          Components: WP7
>    Affects Versions: 1.6.1
>         Environment: VS.NET 2010 and WP7.1 emulator
>            Reporter: Alan Neveu
>            Assignee: Jesse MacFadyen
>            Priority: Minor
>
> I tried submitting my PG 1.5 app to the Windows Marketplace and it was rejected due to WP7's requirements for the Back Button. I upgraded to PG 1.6.1 and I am inspecting how it works with the hardware back button.  It seems to work much better, but my app is designed so that it has a soft back button in the app in various places, and on WP7 the user can always tap the hardware back button.  I am trying to use navigator.app.historyBack and it appears to work, but it does something slightly different than actually tapping the hardware back button does.  I have also tried using window.history.back and that works different yet.  I am using JQueryMobile 1.1.0 and so I wind up doing quite a lot of $.mobile.changePage calls to #Page id's, and because I use multiple .html files I also need to do some rel="external" links or window.location.href= calls.  I think my needs are similar or the same as those of other WP7 developers.  Here is what currently happens with PG 1.6.1 in a simple Page1/Page2 JQueryMobile app when using the hardware back button versus using navigator.app.historyBack, vs. window.history.back:
> SCENARIO #1 - using hardware BackButton only
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> BackButton tap - goes back to Page1 but page is requested again and reloaded from scratch which is slow and the user loses any form data they had entered.
> BackButton tap - exits app (great!)
> SCENARIO #2 - hardware BackButton and navigator.app.backHistory
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> navigator.app.backHistory(); goes back to Page1 served from cache, which is fast and form data is preserved.
> BackButton tap: nothing happens
> BackButton tap: Page1 is reloaded from scratch
> BackButton tap: exits app
> SCENARIO #3 - hardware backButton and window.history.back
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> window.history.back(); goes back to Page1 served from cache.
> BackButton tap: Page1 is reloaded from scratch (DOH!)
> BackButton tap: exits app
> My Observations:
> 1) The hardware back button does not use the cached page - it reloads/re-requests the page. This is kind of a drag but I think we have to just go with this because it is the behavior that the Marketplace testers will be expecting and validating.
> 2) window.history.back() is giving better results than navigator.app.backHistory, but still not the same as the hardware back button. I think apps will fail Marketplace certification if they use either of these approaches for soft back buttons.
> 3) When going back to an external page (as opposed to a JQueryMobile #pageID), window.history.back works but navigator.app.backHistory does not seem to do anything at all.  I say window.history.back "works" but it is still the same result as in Scenario #3 above, which is not good.
> My recommendations:
> 2) Perhaps PhoneGap can enhance the WP7 implementation of navigator.app.backHistory so that it works the same as the hardware back button. It should go back a page and reload that page, and then pressing the hardware back button on Page 1 should exit the app.
> I mark this case as "blocker" priority because it is not possible to get a WP7 app certified for the Marketplace unless the back button works exactly the way they want it to. It seems that for WP7 we have to design our apps so as to keep the history as small as possible so that the number of paths through an app are minimized. We need soft "back" buttons in iOS and BlackBerry, and they are helpful for Android and WP7, and if we can get navigator.app.backHistory to behave (on WP7) identical to the hardware back button, I think we will have forever solved the WP7 back button issue.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira

       

[jira] [Updated] (CB-520) WP7 Certification and the Back Button

Posted by "Jesse MacFadyen (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/CB-520?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Jesse MacFadyen updated CB-520:
-------------------------------

    Fix Version/s: 1.8.0
    
> WP7 Certification and the Back Button
> -------------------------------------
>
>                 Key: CB-520
>                 URL: https://issues.apache.org/jira/browse/CB-520
>             Project: Apache Callback
>          Issue Type: Bug
>          Components: WP7
>    Affects Versions: 1.6.1
>         Environment: VS.NET 2010 and WP7.1 emulator
>            Reporter: Alan Neveu
>            Assignee: Jesse MacFadyen
>            Priority: Minor
>             Fix For: 1.8.0
>
>
> I tried submitting my PG 1.5 app to the Windows Marketplace and it was rejected due to WP7's requirements for the Back Button. I upgraded to PG 1.6.1 and I am inspecting how it works with the hardware back button.  It seems to work much better, but my app is designed so that it has a soft back button in the app in various places, and on WP7 the user can always tap the hardware back button.  I am trying to use navigator.app.historyBack and it appears to work, but it does something slightly different than actually tapping the hardware back button does.  I have also tried using window.history.back and that works different yet.  I am using JQueryMobile 1.1.0 and so I wind up doing quite a lot of $.mobile.changePage calls to #Page id's, and because I use multiple .html files I also need to do some rel="external" links or window.location.href= calls.  I think my needs are similar or the same as those of other WP7 developers.  Here is what currently happens with PG 1.6.1 in a simple Page1/Page2 JQueryMobile app when using the hardware back button versus using navigator.app.historyBack, vs. window.history.back:
> SCENARIO #1 - using hardware BackButton only
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> BackButton tap - goes back to Page1 but page is requested again and reloaded from scratch which is slow and the user loses any form data they had entered.
> BackButton tap - exits app (great!)
> SCENARIO #2 - hardware BackButton and navigator.app.backHistory
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> navigator.app.backHistory(); goes back to Page1 served from cache, which is fast and form data is preserved.
> BackButton tap: nothing happens
> BackButton tap: Page1 is reloaded from scratch
> BackButton tap: exits app
> SCENARIO #3 - hardware backButton and window.history.back
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> window.history.back(); goes back to Page1 served from cache.
> BackButton tap: Page1 is reloaded from scratch (DOH!)
> BackButton tap: exits app
> My Observations:
> 1) The hardware back button does not use the cached page - it reloads/re-requests the page. This is kind of a drag but I think we have to just go with this because it is the behavior that the Marketplace testers will be expecting and validating.
> 2) window.history.back() is giving better results than navigator.app.backHistory, but still not the same as the hardware back button. I think apps will fail Marketplace certification if they use either of these approaches for soft back buttons.
> 3) When going back to an external page (as opposed to a JQueryMobile #pageID), window.history.back works but navigator.app.backHistory does not seem to do anything at all.  I say window.history.back "works" but it is still the same result as in Scenario #3 above, which is not good.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira

       

[jira] [Issue Comment Edited] (CB-520) WP7 Certification and the Back Button

Posted by "Alan Neveu (Issue Comment Edited) (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/CB-520?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13255892#comment-13255892 ] 

Alan Neveu edited comment on CB-520 at 4/18/12 3:38 PM:
--------------------------------------------------------

I have spent time re-reading the certification requirements from Microsoft regarding the (holy) back button. I have also spent time modifying the source for the WP7CordovaClassLib C# project. I created an additional commandCallParams.Action called "softbackbutton". This allows the javascript layer to perform a window.history.back while playing nicely with the hardware back button stack that Cordova maintains.  It is called like this:

cordova.exec(null, null, "CoreEvents", "SoftBackButton", null);

Here is the modified code in CordovaView.xaml.cs that handles commandCallParams, beginning in line 405 of CordovaWP7 1.6.1:

            if (commandCallParams == null)
            {
                // ERROR
                Debug.WriteLine("ScriptNotify :: " + commandStr);
            }
            else if (commandCallParams.Service == "CoreEvents")
            {
                switch (commandCallParams.Action.ToLower())
                {
                    case "overridebackbutton":
                        string args = commandCallParams.Args;
                        this.OverrideBackButton = (args != null && args.Length > 0 && args.ToLower() == "true");
                        break;
                }
                if (commandCallParams.Action.ToLower() == "softbackbutton")
                {
                    try
                    {
                        CordovaBrowser.InvokeScript("eval", "window.history.back()");
                        history.Pop();
                    }
                    catch (Exception ex)
                    {
                        //do nothing, must have been called from first page, not our fault here.
                    }
                }
            }
            else
            {
                //Debug.WriteLine("ProcessCommand :: " + commandStr);
                this.nativeExecution.ProcessCommand(commandCallParams);
            }

                
      was (Author: offshorewahoo):
    I have spent time re-reading the certification requirements from Microsoft regarding the (holy) back button. I have also spent time modifying the source for the WP7CordovaClassLib C# project. I created an additional commandCallParams.Service called "CustomHack" with an action of "historypop". This allows javascript to do a simple window.history.back() and then also call:

cordova.exec(null, null, "CustomHack", "HistoryPop", null);

Which essentially tells the history stack that we have gone back one page.  Then when you tap the hardware back button it only takes tapping it once, rather than twice (or even more times depending on how many times you did a window.history.back).

I know this is ugly, but here is my code that adds the commandCallParams.Service for "CustomHack"

            if (commandCallParams == null)
            {
                // ERROR
                Debug.WriteLine("ScriptNotify :: " + commandStr);
            }
            else if (commandCallParams.Service == "CoreEvents")
            {
                switch (commandCallParams.Action.ToLower())
                {
                    case "overridebackbutton":
                        string args = commandCallParams.Args;
                        this.OverrideBackButton = (args != null && args.Length > 0 && args.ToLower() == "true");
                        break;
                }
            }
            else if (commandCallParams.Service == "CustomHack")
            {
                if (commandCallParams.Action.ToLower() == "historypop")
                {
                    if (history.Count > 1)
                    {
                        history.Pop();
                    }
                }
            }
            else
            {
                //Debug.WriteLine("ProcessCommand :: " + commandStr);
                this.nativeExecution.ProcessCommand(commandCallParams);
            }


I say this is "ugly" because I have intentionally used "CustomHack" for the commandCallParams.Service string. Probably using commandCallParams is not the method that you would choose to implement such a feature as this.  So that is why I named it intentionally ugly so that you can see what I am recommending and implement as you see fit.  As long as we can call it easily from javascript, that is really all we need.  So, in javascript we wind up using this as follows:

cordova.exec(null, null, "CustomHack", "HistoryPop", null);
window.history.back();

If you want to pull the window.history.back() into the CordovaClass too, that would be a fine idea, so that using this would just be one line of code from javascript.

I played around with doing things like replicating the operative code in page_BackKeyPress, as follows:

history.Pop();
Uri next = history.Peek();
IsBackButtonPressed = true;
CordovaBrowser.Navigate(next);

and I even tried looping through page_BackKeyPress instead of copying/pasting those lines of code. But it seems that the hardware back button is also doing something *special* in native/external code even before the Silverlight project gets to do its magic and override it, and the result was just not quite right.  So I think the best thing is to just use the simple window.history.back and inform the history stack of our action.

Love to hear your thoughts!
                  
> WP7 Certification and the Back Button
> -------------------------------------
>
>                 Key: CB-520
>                 URL: https://issues.apache.org/jira/browse/CB-520
>             Project: Apache Callback
>          Issue Type: Bug
>          Components: WP7
>    Affects Versions: 1.6.1
>         Environment: VS.NET 2010 and WP7.1 emulator
>            Reporter: Alan Neveu
>            Assignee: Jesse MacFadyen
>            Priority: Blocker
>
> I tried submitting my PG 1.5 app to the Windows Marketplace and it was rejected due to WP7's requirements for the Back Button. I upgraded to PG 1.6.1 and I am inspecting how it works with the hardware back button.  It seems to work much better, but my app is designed so that it has a soft back button in the app in various places, and on WP7 the user can always tap the hardware back button.  I am trying to use navigator.app.historyBack and it appears to work, but it does something slightly different than actually tapping the hardware back button does.  I have also tried using window.history.back and that works different yet.  I am using JQueryMobile 1.1.0 and so I wind up doing quite a lot of $.mobile.changePage calls to #Page id's, and because I use multiple .html files I also need to do some rel="external" links or window.location.href= calls.  I think my needs are similar or the same as those of other WP7 developers.  Here is what currently happens with PG 1.6.1 in a simple Page1/Page2 JQueryMobile app when using the hardware back button versus using navigator.app.historyBack, vs. window.history.back:
> SCENARIO #1 - using hardware BackButton only
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> BackButton tap - goes back to Page1 but page is requested again and reloaded from scratch which is slow and the user loses any form data they had entered.
> BackButton tap - exits app (great!)
> SCENARIO #2 - hardware BackButton and navigator.app.backHistory
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> navigator.app.backHistory(); goes back to Page1 served from cache, which is fast and form data is preserved.
> BackButton tap: nothing happens
> BackButton tap: Page1 is reloaded from scratch
> BackButton tap: exits app
> SCENARIO #3 - hardware backButton and window.history.back
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> window.history.back(); goes back to Page1 served from cache.
> BackButton tap: Page1 is reloaded from scratch (DOH!)
> BackButton tap: exits app
> My Observations:
> 1) The hardware back button does not use the cached page - it reloads/re-requests the page. This is kind of a drag but I think we have to just go with this because it is the behavior that the Marketplace testers will be expecting and validating.
> 2) window.history.back() is giving better results than navigator.app.backHistory, but still not the same as the hardware back button. I think apps will fail Marketplace certification if they use either of these approaches for soft back buttons.
> 3) When going back to an external page (as opposed to a JQueryMobile #pageID), window.history.back works but navigator.app.backHistory does not seem to do anything at all.  I say window.history.back "works" but it is still the same result as in Scenario #3 above, which is not good.
> My recommendations:
> 2) Perhaps PhoneGap can enhance the WP7 implementation of navigator.app.backHistory so that it works the same as the hardware back button. It should go back a page and reload that page, and then pressing the hardware back button on Page 1 should exit the app.
> I mark this case as "blocker" priority because it is not possible to get a WP7 app certified for the Marketplace unless the back button works exactly the way they want it to. It seems that for WP7 we have to design our apps so as to keep the history as small as possible so that the number of paths through an app are minimized. We need soft "back" buttons in iOS and BlackBerry, and they are helpful for Android and WP7, and if we can get navigator.app.backHistory to behave (on WP7) identical to the hardware back button, I think we will have forever solved the WP7 back button issue.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira

       

[jira] [Issue Comment Edited] (CB-520) WP7 Certification and the Back Button

Posted by "Alan Neveu (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/CB-520?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13256926#comment-13256926 ] 

Alan Neveu edited comment on CB-520 at 4/23/12 7:58 PM:
--------------------------------------------------------

I wound up modifying CordovaView.xaml.cs in WP7CordovaClassLib.  It turns out WP7 apps really need to be able to have more control over the history stack in order to comply with Marketplace tech cert requirements.  Here is the source I wound up settling on for CordovaView.xaml.cs...  Note the enhancements to void GapBrowser_ScriptNotify, and also to void page_BackKeyPress.

/*  
	Licensed under the Apache License, Version 2.0 (the "License");
	you may not use this file except in compliance with the License.
	You may obtain a copy of the License at
	
	http://www.apache.org/licenses/LICENSE-2.0
	
	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using System.IO.IsolatedStorage;
using System.Windows.Resources;
using System.Windows.Interop;
using System.Runtime.Serialization.Json;
using System.IO;
using System.ComponentModel;
using System.Xml.Linq;
using WP7CordovaClassLib.Cordova.Commands;
using System.Diagnostics;
using System.Text;
using WP7CordovaClassLib.Cordova;
using System.Threading;
using Microsoft.Phone.Shell;



namespace WP7CordovaClassLib
{
    public partial class CordovaView : UserControl
    {
       
        /// <summary>
        /// Indicates whether web control has been loaded and no additional initialization is needed.
        /// Prevents data clearing during page transitions.
        /// </summary>
        private bool IsBrowserInitialized = false;
        
        /// <summary>
        /// Set when the user attaches a back button handler inside the WebBrowser
        /// </summary>
        private bool OverrideBackButton = false;

        /// <summary>
        /// Used for keeping track of our history
        /// </summary>
        private Stack<Uri> history = new Stack<Uri>();
        private bool IsBackButtonPressed = false;


        private static string AppRoot = "/app/";


        /// <summary>
        /// Handles native api calls
        /// </summary>
        private NativeExecution nativeExecution;

        protected DOMStorageHelper domStorageHelper;
        protected OrientationHelper orientationHelper;

        public System.Windows.Controls.Grid _LayoutRoot
        {
            get
            {
                return ((System.Windows.Controls.Grid)(this.FindName("LayoutRoot")));
            }
        }

        public WebBrowser Browser
        {
            get
            {
                return CordovaBrowser;
            }
        }

        /*
         * Setting StartPageUri only has an effect if called before the view is loaded.
         **/
        protected Uri _startPageUri = null;
        public Uri StartPageUri
        {
            get
            {
                if (_startPageUri == null)
                {
                    // default
                    return new Uri( AppRoot + "www/index.html", UriKind.Relative);                    
                }
                else
                {
                    return _startPageUri;
                }
            }
            set
            {
                if (!this.IsBrowserInitialized)
                {
                    _startPageUri = value;
                }
            }
        }

        public CordovaView()
        {
            
            InitializeComponent();

            if (DesignerProperties.IsInDesignTool)
            {
                return;
            }


            StartupMode mode = PhoneApplicationService.Current.StartupMode;

            if (mode == StartupMode.Launch)
            {
                PhoneApplicationService service = PhoneApplicationService.Current;
                service.Activated += new EventHandler<Microsoft.Phone.Shell.ActivatedEventArgs>(AppActivated);
                service.Launching += new EventHandler<LaunchingEventArgs>(AppLaunching);
                service.Deactivated += new EventHandler<DeactivatedEventArgs>(AppDeactivated);
                service.Closing += new EventHandler<ClosingEventArgs>(AppClosing);
            }
            else
            {

            }

            // initializes native execution logic
            this.nativeExecution = new NativeExecution(ref this.CordovaBrowser);
        }

        

        void AppClosing(object sender, ClosingEventArgs e)
        {
            Debug.WriteLine("AppClosing");
        }

        void AppDeactivated(object sender, DeactivatedEventArgs e)
        {
            Debug.WriteLine("AppDeactivated");

            try
            {
                CordovaBrowser.InvokeScript("CordovaCommandResult", new string[] { "pause" });
            }
            catch (Exception)
            {
                Debug.WriteLine("Pause event error");
            } 
        }

        void AppLaunching(object sender, LaunchingEventArgs e)
        {
            Debug.WriteLine("AppLaunching");
        }

        void AppActivated(object sender, Microsoft.Phone.Shell.ActivatedEventArgs e)
        {
            Debug.WriteLine("AppActivated");
            try
            {
                CordovaBrowser.InvokeScript("CordovaCommandResult", new string[] { "resume" });
            }
            catch (Exception)
            {
                Debug.WriteLine("Resume event error");
            }  
        }

        void GapBrowser_Loaded(object sender, RoutedEventArgs e)
        {
            if (DesignerProperties.IsInDesignTool)
            {
                return;
            }

            // prevents refreshing web control to initial state during pages transitions
            if (this.IsBrowserInitialized) return;

            this.domStorageHelper = new DOMStorageHelper(this.CordovaBrowser);

            try
            {

                // Before we possibly clean the ISO-Store, we need to grab our generated UUID, so we can rewrite it after.
                string deviceUUID = "";

                using (IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    try
                    {
                        IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream("DeviceID.txt", FileMode.Open, FileAccess.Read, appStorage);

                        using (StreamReader reader = new StreamReader(fileStream))
                        {
                            deviceUUID = reader.ReadLine();
                        }
                    }
                    catch (Exception /*ex*/)
                    {
                        deviceUUID = Guid.NewGuid().ToString();
                    }

                    Debug.WriteLine("Updating IsolatedStorage for APP:DeviceID :: " + deviceUUID);
                    IsolatedStorageFileStream file = new IsolatedStorageFileStream("DeviceID.txt", FileMode.Create, FileAccess.Write, appStorage);
                    using (StreamWriter writeFile = new StreamWriter(file))
                    {
                        writeFile.WriteLine(deviceUUID);
                        writeFile.Close();
                    }
   
                }

                StreamResourceInfo streamInfo = Application.GetResourceStream(new Uri("CordovaSourceDictionary.xml", UriKind.Relative));

                if (streamInfo != null)
                {
                    StreamReader sr = new StreamReader(streamInfo.Stream);
                    //This will Read Keys Collection for the xml file

                    XDocument document = XDocument.Parse(sr.ReadToEnd());

                    var files = from results in document.Descendants("FilePath")
                                 select new
                                 {
                                     path =  (string)results.Attribute("Value")
                                 };
                    StreamResourceInfo fileResourceStreamInfo;

                    using (IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication())
                    {

                        foreach (var file in files)
                        {
                            fileResourceStreamInfo = Application.GetResourceStream(new Uri(file.path, UriKind.Relative));

                            if (fileResourceStreamInfo != null)
                            {
                                using (BinaryReader br = new BinaryReader(fileResourceStreamInfo.Stream))
                                {
                                    byte[] data = br.ReadBytes((int)fileResourceStreamInfo.Stream.Length);

                                    string strBaseDir = AppRoot + file.path.Substring(0, file.path.LastIndexOf(System.IO.Path.DirectorySeparatorChar));

                                    if(!appStorage.DirectoryExists(strBaseDir))
                                    {
                                        //Debug.WriteLine("Creating Directory :: " + strBaseDir);
                                        appStorage.CreateDirectory(strBaseDir);
                                    }

                                    // This will truncate/overwrite an existing file, or 
                                    using (IsolatedStorageFileStream outFile = appStorage.OpenFile(AppRoot + file.path, FileMode.Create))
                                    {
                                        Debug.WriteLine("Writing data for " + AppRoot + file.path + " and length = " + data.Length);
                                        using (var writer = new BinaryWriter(outFile))
                                        {
                                            writer.Write(data);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                Debug.WriteLine("Failed to write file :: " + file.path + " did you forget to add it to the project?");
                            }
                        }
                    }
                }

                CordovaBrowser.Navigate(StartPageUri);
                IsBrowserInitialized = true;
                AttachHardwareButtonHandlers();
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception in GapBrowser_Loaded :: {0}", ex.Message);
            }
        }

        void AttachHardwareButtonHandlers()
        {
            PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
            if (frame != null)
            {
                PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
                 
                if (page != null)
                {
                    page.BackKeyPress += new EventHandler<CancelEventArgs>(page_BackKeyPress);

                    this.orientationHelper = new OrientationHelper(this.CordovaBrowser, page); 

                }
            }
        }

        void page_BackKeyPress(object sender, CancelEventArgs e)
        {
            if (OverrideBackButton)
            {
                try
                {
                    CordovaBrowser.InvokeScript("CordovaCommandResult", new string[] { "backbutton" });
                    e.Cancel = true;
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Exception while invoking backbutton into cordova view: " + ex.Message);
                }
            }
            else
            {
                if (history.Count > 1)
                {
                    Uri current = history.Peek();
                    history.Pop();
                    Uri next = history.Peek();
                    Debug.WriteLine("current = " + current + "   next = " + next);
                    int curHash = current.ToString().IndexOf("#");
                    int nextHash = next.ToString().IndexOf("#");
                    if (curHash > 0 && nextHash > 0 && curHash == nextHash)
                    {
                        Debug.WriteLine("doing window.history.back()");
                        IsBackButtonPressed = false;
                        CordovaBrowser.InvokeScript("eval", "window.history.back()");
                        if (next.ToString().IndexOf("_wpBackReload") > -1)
                        {
                            CordovaBrowser.InvokeScript("eval", "window.location.reload()");
                        }
                    }
                    else
                    {
                        Debug.WriteLine("Doing Navigate");
                        if (next.ToString().IndexOf("_wpBackReload") > -1)
                        {
                            Uri last = history.Last();
                            history.Clear();
                            history.Push(last);
                        }
                        IsBackButtonPressed = true;
                        CordovaBrowser.Navigate(next);
                    }
                    e.Cancel = true;
                }
            }
        }

        void GapBrowser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
        {
            this.CordovaBrowser.Opacity = 1;

            string nativeReady = "(function(){ cordova.require('cordova/channel').onNativeReady.fire()})();";

            try
            {
                CordovaBrowser.InvokeScript("execScript", new string[] { nativeReady });
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Error calling js to fire nativeReady event. Did you include cordova-x.x.x.js in your html script tag?");
            }
        }


        void GapBrowser_Navigating(object sender, NavigatingEventArgs e)
        {
            if (!IsBackButtonPressed)
            {
                history.Push(e.Uri);
            }
            else
            {
                IsBackButtonPressed = false;
            }

            Debug.WriteLine("GapBrowser_Navigating to :: " + e.Uri.ToString());
            // TODO: tell any running plugins to stop doing what they are doing.
            // TODO: check whitelist / blacklist
            // NOTE: Navigation can be cancelled by setting :        e.Cancel = true;


        }

        /*
         *  This method does the work of routing commands
         *  NotifyEventArgs.Value contains a string passed from JS 
         *  If the command already exists in our map, we will just attempt to call the method(action) specified, and pass the args along
         *  Otherwise, we create a new instance of the command, add it to the map, and call it ...
         *  This method may also receive JS error messages caught by window.onerror, in any case where the commandStr does not appear to be a valid command
         *  it is simply output to the debugger output, and the method returns.
         * 
         **/
        void GapBrowser_ScriptNotify(object sender, NotifyEventArgs e)
        {
            string commandStr = e.Value;

            if (commandStr.IndexOf("DOMStorage") == 0)
            {
                this.domStorageHelper.HandleStorageCommand(commandStr);
                return;
            }
            else if (commandStr.IndexOf("Orientation") == 0)
            {
                this.orientationHelper.HandleCommand(commandStr);
                return;
            }

            CordovaCommandCall commandCallParams = CordovaCommandCall.Parse(commandStr);

            if (commandCallParams == null)
            {
                // ERROR
                Debug.WriteLine("ScriptNotify :: " + commandStr);
            }
            else if (commandCallParams.Service == "CoreEvents")
            {
                switch (commandCallParams.Action.ToLower())
                {
                    case "overridebackbutton":
                        string args = commandCallParams.Args;
                        this.OverrideBackButton = (args != null && args.Length > 0 && args.ToLower() == "true");
                        break;
                }
                if (commandCallParams.Action.ToLower() == "softbackbutton")
                {
                    try
                    {
                        CordovaBrowser.InvokeScript("eval", "window.history.back()");
                        history.Pop();
                    }
                    catch (Exception ex)
                    {
                        //do nothing, must have been called from first page, not our fault here.
                    }
                }
                if (commandCallParams.Action.ToLower() == "historystackpop")
                {
                    try
                    {
                        history.Pop();
                    }
                    catch (Exception ex)
                    {
                        //do nothing, must have been called from first page, not our fault here.
                    }
                }
                if (commandCallParams.Action.ToLower() == "historystackclear")
                {
                    try
                    {
                        history.Clear();
                    }
                    catch (Exception ex)
                    {
                        //do nothing, must have been called from first page, not our fault here.
                    }
                }
            }
            else
            {
                //Debug.WriteLine("ProcessCommand :: " + commandStr);
                this.nativeExecution.ProcessCommand(commandCallParams);
            }
        }

        private void GapBrowser_Unloaded(object sender, RoutedEventArgs e)
        {

        }

        private void GapBrowser_NavigationFailed(object sender, System.Windows.Navigation.NavigationFailedEventArgs e)
        {
            Debug.WriteLine("GapBrowser_NavigationFailed :: " + e.Uri.ToString());
        }

        private void GapBrowser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
        {
            Debug.WriteLine("GapBrowser_Navigated :: " + e.Uri.ToString());
        }

       
    }
}


                
      was (Author: offshorewahoo):
    I wound up modifying CordovaView.xaml.cs in WP7CordovaClassLib.  It turns out WP7 apps really need to be able to have more control over the history stack in order to comply with Marketplace tech cert requirements.  Here is the source I wound up settling on for CordovaView.xaml.cs...  Note the enhancements to void GapBrowser_ScriptNotify, and also to void page_BackKeyPress.

/*  
	Licensed under the Apache License, Version 2.0 (the "License");
	you may not use this file except in compliance with the License.
	You may obtain a copy of the License at
	
	http://www.apache.org/licenses/LICENSE-2.0
	
	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using System.IO.IsolatedStorage;
using System.Windows.Resources;
using System.Windows.Interop;
using System.Runtime.Serialization.Json;
using System.IO;
using System.ComponentModel;
using System.Xml.Linq;
using WP7CordovaClassLib.Cordova.Commands;
using System.Diagnostics;
using System.Text;
using WP7CordovaClassLib.Cordova;
using System.Threading;
using Microsoft.Phone.Shell;



namespace WP7CordovaClassLib
{
    public partial class CordovaView : UserControl
    {
       
        /// <summary>
        /// Indicates whether web control has been loaded and no additional initialization is needed.
        /// Prevents data clearing during page transitions.
        /// </summary>
        private bool IsBrowserInitialized = false;
        
        /// <summary>
        /// Set when the user attaches a back button handler inside the WebBrowser
        /// </summary>
        private bool OverrideBackButton = false;

        /// <summary>
        /// Used for keeping track of our history
        /// </summary>
        private Stack<Uri> history = new Stack<Uri>();
        private bool IsBackButtonPressed = false;


        private static string AppRoot = "/app/";


        /// <summary>
        /// Handles native api calls
        /// </summary>
        private NativeExecution nativeExecution;

        protected DOMStorageHelper domStorageHelper;
        protected OrientationHelper orientationHelper;

        public System.Windows.Controls.Grid _LayoutRoot
        {
            get
            {
                return ((System.Windows.Controls.Grid)(this.FindName("LayoutRoot")));
            }
        }

        public WebBrowser Browser
        {
            get
            {
                return CordovaBrowser;
            }
        }

        /*
         * Setting StartPageUri only has an effect if called before the view is loaded.
         **/
        protected Uri _startPageUri = null;
        public Uri StartPageUri
        {
            get
            {
                if (_startPageUri == null)
                {
                    // default
                    return new Uri( AppRoot + "www/index.html", UriKind.Relative);                    
                }
                else
                {
                    return _startPageUri;
                }
            }
            set
            {
                if (!this.IsBrowserInitialized)
                {
                    _startPageUri = value;
                }
            }
        }

        public CordovaView()
        {
            
            InitializeComponent();

            if (DesignerProperties.IsInDesignTool)
            {
                return;
            }


            StartupMode mode = PhoneApplicationService.Current.StartupMode;

            if (mode == StartupMode.Launch)
            {
                PhoneApplicationService service = PhoneApplicationService.Current;
                service.Activated += new EventHandler<Microsoft.Phone.Shell.ActivatedEventArgs>(AppActivated);
                service.Launching += new EventHandler<LaunchingEventArgs>(AppLaunching);
                service.Deactivated += new EventHandler<DeactivatedEventArgs>(AppDeactivated);
                service.Closing += new EventHandler<ClosingEventArgs>(AppClosing);
            }
            else
            {

            }

            // initializes native execution logic
            this.nativeExecution = new NativeExecution(ref this.CordovaBrowser);
        }

        

        void AppClosing(object sender, ClosingEventArgs e)
        {
            Debug.WriteLine("AppClosing");
        }

        void AppDeactivated(object sender, DeactivatedEventArgs e)
        {
            Debug.WriteLine("AppDeactivated");

            try
            {
                CordovaBrowser.InvokeScript("CordovaCommandResult", new string[] { "pause" });
            }
            catch (Exception)
            {
                Debug.WriteLine("Pause event error");
            } 
        }

        void AppLaunching(object sender, LaunchingEventArgs e)
        {
            Debug.WriteLine("AppLaunching");
        }

        void AppActivated(object sender, Microsoft.Phone.Shell.ActivatedEventArgs e)
        {
            Debug.WriteLine("AppActivated");
            try
            {
                CordovaBrowser.InvokeScript("CordovaCommandResult", new string[] { "resume" });
            }
            catch (Exception)
            {
                Debug.WriteLine("Resume event error");
            }  
        }

        void GapBrowser_Loaded(object sender, RoutedEventArgs e)
        {
            if (DesignerProperties.IsInDesignTool)
            {
                return;
            }

            // prevents refreshing web control to initial state during pages transitions
            if (this.IsBrowserInitialized) return;

            this.domStorageHelper = new DOMStorageHelper(this.CordovaBrowser);

            try
            {

                // Before we possibly clean the ISO-Store, we need to grab our generated UUID, so we can rewrite it after.
                string deviceUUID = "";

                using (IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    try
                    {
                        IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream("DeviceID.txt", FileMode.Open, FileAccess.Read, appStorage);

                        using (StreamReader reader = new StreamReader(fileStream))
                        {
                            deviceUUID = reader.ReadLine();
                        }
                    }
                    catch (Exception /*ex*/)
                    {
                        deviceUUID = Guid.NewGuid().ToString();
                    }

                    Debug.WriteLine("Updating IsolatedStorage for APP:DeviceID :: " + deviceUUID);
                    IsolatedStorageFileStream file = new IsolatedStorageFileStream("DeviceID.txt", FileMode.Create, FileAccess.Write, appStorage);
                    using (StreamWriter writeFile = new StreamWriter(file))
                    {
                        writeFile.WriteLine(deviceUUID);
                        writeFile.Close();
                    }
   
                }

                StreamResourceInfo streamInfo = Application.GetResourceStream(new Uri("CordovaSourceDictionary.xml", UriKind.Relative));

                if (streamInfo != null)
                {
                    StreamReader sr = new StreamReader(streamInfo.Stream);
                    //This will Read Keys Collection for the xml file

                    XDocument document = XDocument.Parse(sr.ReadToEnd());

                    var files = from results in document.Descendants("FilePath")
                                 select new
                                 {
                                     path =  (string)results.Attribute("Value")
                                 };
                    StreamResourceInfo fileResourceStreamInfo;

                    using (IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication())
                    {

                        foreach (var file in files)
                        {
                            fileResourceStreamInfo = Application.GetResourceStream(new Uri(file.path, UriKind.Relative));

                            if (fileResourceStreamInfo != null)
                            {
                                using (BinaryReader br = new BinaryReader(fileResourceStreamInfo.Stream))
                                {
                                    byte[] data = br.ReadBytes((int)fileResourceStreamInfo.Stream.Length);

                                    string strBaseDir = AppRoot + file.path.Substring(0, file.path.LastIndexOf(System.IO.Path.DirectorySeparatorChar));

                                    if(!appStorage.DirectoryExists(strBaseDir))
                                    {
                                        //Debug.WriteLine("Creating Directory :: " + strBaseDir);
                                        appStorage.CreateDirectory(strBaseDir);
                                    }

                                    // This will truncate/overwrite an existing file, or 
                                    using (IsolatedStorageFileStream outFile = appStorage.OpenFile(AppRoot + file.path, FileMode.Create))
                                    {
                                        Debug.WriteLine("Writing data for " + AppRoot + file.path + " and length = " + data.Length);
                                        using (var writer = new BinaryWriter(outFile))
                                        {
                                            writer.Write(data);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                Debug.WriteLine("Failed to write file :: " + file.path + " did you forget to add it to the project?");
                            }
                        }
                    }
                }

                CordovaBrowser.Navigate(StartPageUri);
                IsBrowserInitialized = true;
                AttachHardwareButtonHandlers();
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception in GapBrowser_Loaded :: {0}", ex.Message);
            }
        }

        void AttachHardwareButtonHandlers()
        {
            PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
            if (frame != null)
            {
                PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
                 
                if (page != null)
                {
                    page.BackKeyPress += new EventHandler<CancelEventArgs>(page_BackKeyPress);

                    this.orientationHelper = new OrientationHelper(this.CordovaBrowser, page); 

                }
            }
        }

        void page_BackKeyPress(object sender, CancelEventArgs e)
        {
            if (OverrideBackButton)
            {
                try
                {
                    CordovaBrowser.InvokeScript("CordovaCommandResult", new string[] { "backbutton" });
                    e.Cancel = true;
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Exception while invoking backbutton into cordova view: " + ex.Message);
                }
            }
            else
            {
                if (history.Count > 1)
                {
                    Uri current = history.Peek();
                    history.Pop();
                    Uri next = history.Peek();
                    IsBackButtonPressed = true;
                    bool doHistory = false;
                    if (current.ToString().IndexOf("#") > 0 && next.ToString().IndexOf("#") > 0)
                    {
                        if (current.ToString().Substring(0, current.ToString().IndexOf("#")) == next.ToString().Substring(0, next.ToString().IndexOf("#")))
                        {
                            doHistory = true;
                        }
                    }
                    if (doHistory)
                    {
                        CordovaBrowser.InvokeScript("eval", "window.history.back()");
                        CordovaBrowser.InvokeScript("eval", "window.location.reload()");
                    }
                    else
                    {
                        CordovaBrowser.Navigate(next);
                    }
                    e.Cancel = true;
                }
            }
        }

        void GapBrowser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
        {
            this.CordovaBrowser.Opacity = 1;

            string nativeReady = "(function(){ cordova.require('cordova/channel').onNativeReady.fire()})();";

            try
            {
                CordovaBrowser.InvokeScript("execScript", new string[] { nativeReady });
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Error calling js to fire nativeReady event. Did you include cordova-x.x.x.js in your html script tag?");
            }
        }


        void GapBrowser_Navigating(object sender, NavigatingEventArgs e)
        {
            if (!IsBackButtonPressed)
            {
                history.Push(e.Uri);
            }
            else
            {
                IsBackButtonPressed = false;
            }

            Debug.WriteLine("GapBrowser_Navigating to :: " + e.Uri.ToString());
            // TODO: tell any running plugins to stop doing what they are doing.
            // TODO: check whitelist / blacklist
            // NOTE: Navigation can be cancelled by setting :        e.Cancel = true;


        }

        /*
         *  This method does the work of routing commands
         *  NotifyEventArgs.Value contains a string passed from JS 
         *  If the command already exists in our map, we will just attempt to call the method(action) specified, and pass the args along
         *  Otherwise, we create a new instance of the command, add it to the map, and call it ...
         *  This method may also receive JS error messages caught by window.onerror, in any case where the commandStr does not appear to be a valid command
         *  it is simply output to the debugger output, and the method returns.
         * 
         **/
        void GapBrowser_ScriptNotify(object sender, NotifyEventArgs e)
        {
            string commandStr = e.Value;

            if (commandStr.IndexOf("DOMStorage") == 0)
            {
                this.domStorageHelper.HandleStorageCommand(commandStr);
                return;
            }
            else if (commandStr.IndexOf("Orientation") == 0)
            {
                this.orientationHelper.HandleCommand(commandStr);
                return;
            }

            CordovaCommandCall commandCallParams = CordovaCommandCall.Parse(commandStr);

            if (commandCallParams == null)
            {
                // ERROR
                Debug.WriteLine("ScriptNotify :: " + commandStr);
            }
            else if (commandCallParams.Service == "CoreEvents")
            {
                switch (commandCallParams.Action.ToLower())
                {
                    case "overridebackbutton":
                        string args = commandCallParams.Args;
                        this.OverrideBackButton = (args != null && args.Length > 0 && args.ToLower() == "true");
                        break;
                }
                if (commandCallParams.Action.ToLower() == "softbackbutton")
                {
                    try
                    {
                        CordovaBrowser.InvokeScript("eval", "window.history.back()");
                        history.Pop();
                    }
                    catch (Exception ex)
                    {
                        //do nothing, must have been called from first page, not our fault here.
                    }
                }
                if (commandCallParams.Action.ToLower() == "historystackpop")
                {
                    try
                    {
                        history.Pop();
                    }
                    catch (Exception ex)
                    {
                        //do nothing, must have been called from first page, not our fault here.
                    }
                }
                if (commandCallParams.Action.ToLower() == "historystackclear")
                {
                    try
                    {
                        history = new Stack<Uri>();
                    }
                    catch (Exception ex)
                    {
                        //do nothing, must have been called from first page, not our fault here.
                    }
                }
            }
            else
            {
                //Debug.WriteLine("ProcessCommand :: " + commandStr);
                this.nativeExecution.ProcessCommand(commandCallParams);
            }
        }

        private void GapBrowser_Unloaded(object sender, RoutedEventArgs e)
        {

        }

        private void GapBrowser_NavigationFailed(object sender, System.Windows.Navigation.NavigationFailedEventArgs e)
        {
            Debug.WriteLine("GapBrowser_NavigationFailed :: " + e.Uri.ToString());
        }

        private void GapBrowser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
        {
            Debug.WriteLine("GapBrowser_Navigated :: " + e.Uri.ToString());
        }

       
    }
}

                  
> WP7 Certification and the Back Button
> -------------------------------------
>
>                 Key: CB-520
>                 URL: https://issues.apache.org/jira/browse/CB-520
>             Project: Apache Callback
>          Issue Type: Bug
>          Components: WP7
>    Affects Versions: 1.6.1
>         Environment: VS.NET 2010 and WP7.1 emulator
>            Reporter: Alan Neveu
>            Assignee: Jesse MacFadyen
>            Priority: Minor
>
> I tried submitting my PG 1.5 app to the Windows Marketplace and it was rejected due to WP7's requirements for the Back Button. I upgraded to PG 1.6.1 and I am inspecting how it works with the hardware back button.  It seems to work much better, but my app is designed so that it has a soft back button in the app in various places, and on WP7 the user can always tap the hardware back button.  I am trying to use navigator.app.historyBack and it appears to work, but it does something slightly different than actually tapping the hardware back button does.  I have also tried using window.history.back and that works different yet.  I am using JQueryMobile 1.1.0 and so I wind up doing quite a lot of $.mobile.changePage calls to #Page id's, and because I use multiple .html files I also need to do some rel="external" links or window.location.href= calls.  I think my needs are similar or the same as those of other WP7 developers.  Here is what currently happens with PG 1.6.1 in a simple Page1/Page2 JQueryMobile app when using the hardware back button versus using navigator.app.historyBack, vs. window.history.back:
> SCENARIO #1 - using hardware BackButton only
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> BackButton tap - goes back to Page1 but page is requested again and reloaded from scratch which is slow and the user loses any form data they had entered.
> BackButton tap - exits app (great!)
> SCENARIO #2 - hardware BackButton and navigator.app.backHistory
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> navigator.app.backHistory(); goes back to Page1 served from cache, which is fast and form data is preserved.
> BackButton tap: nothing happens
> BackButton tap: Page1 is reloaded from scratch
> BackButton tap: exits app
> SCENARIO #3 - hardware backButton and window.history.back
> Page 1 links to Page 2 using $.mobile.changePage("#Page2");
> window.history.back(); goes back to Page1 served from cache.
> BackButton tap: Page1 is reloaded from scratch (DOH!)
> BackButton tap: exits app
> My Observations:
> 1) The hardware back button does not use the cached page - it reloads/re-requests the page. This is kind of a drag but I think we have to just go with this because it is the behavior that the Marketplace testers will be expecting and validating.
> 2) window.history.back() is giving better results than navigator.app.backHistory, but still not the same as the hardware back button. I think apps will fail Marketplace certification if they use either of these approaches for soft back buttons.
> 3) When going back to an external page (as opposed to a JQueryMobile #pageID), window.history.back works but navigator.app.backHistory does not seem to do anything at all.  I say window.history.back "works" but it is still the same result as in Scenario #3 above, which is not good.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira