Fixing touch scrolling support for devices that support it natively. Our current touch support overrides native touch scrolling on tablets, which breaks touch scrolling.

Review at http://gwt-code-reviews.appspot.com/1369809

Review by: pdr@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9852 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/event/dom/DomEvent.gwt.xml b/user/src/com/google/gwt/event/dom/DomEvent.gwt.xml
index 1e761c5..12d921e 100644
--- a/user/src/com/google/gwt/event/dom/DomEvent.gwt.xml
+++ b/user/src/com/google/gwt/event/dom/DomEvent.gwt.xml
@@ -1,5 +1,6 @@
 <module>
-	<inherits name="com.google.gwt.event.EventBase" />
-	<inherits name="com.google.gwt.dom.DOM" />
+  <inherits name="com.google.gwt.dom.DOM" />
+  <inherits name="com.google.gwt.event.EventBase" />
+  <inherits name="com.google.gwt.event.dom.TouchEvent" />
   <source path="client"/>
 </module>
diff --git a/user/src/com/google/gwt/event/dom/TouchEvent.gwt.xml b/user/src/com/google/gwt/event/dom/TouchEvent.gwt.xml
new file mode 100644
index 0000000..e2beb0b
--- /dev/null
+++ b/user/src/com/google/gwt/event/dom/TouchEvent.gwt.xml
@@ -0,0 +1,37 @@
+<!--                                                                        -->
+<!-- Copyright 2011 Google Inc.                                             -->
+<!-- Licensed under the Apache License, Version 2.0 (the "License"); you    -->
+<!-- may not use this file except in compliance with the License. You may   -->
+<!-- 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. License for the specific language governing permissions and   -->
+<!-- limitations under the License.                                         -->
+<module>
+  <inherits name="com.google.gwt.user.User" />
+
+  <!-- Define the support property -->
+  <define-property name="touchEventSupport" values="maybe,no" />
+
+  <!--
+    Modern browsers either support touch events or will probably add touch
+    support in the future.
+  -->
+  <set-property name="touchEventSupport" value="maybe" />
+
+  <!-- Older browsers do not support touch events. -->
+  <set-property name="touchEventSupport" value="no">
+    <any>
+      <when-property-is name="user.agent" value="ie6" />
+    </any>
+  </set-property>
+
+  <replace-with class="com.google.gwt.event.dom.TouchEvent.TouchSupportDetectorNo">
+    <when-type-is class="com.google.gwt.event.dom.TouchEvent.TouchSupportDetector" />
+    <when-property-is name="touchEventSupport" value="no" />
+  </replace-with>
+</module>
diff --git a/user/src/com/google/gwt/event/dom/client/TouchEvent.java b/user/src/com/google/gwt/event/dom/client/TouchEvent.java
index e59c69d..556c3cb 100644
--- a/user/src/com/google/gwt/event/dom/client/TouchEvent.java
+++ b/user/src/com/google/gwt/event/dom/client/TouchEvent.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.event.dom.client;
 
+import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.JsArray;
 import com.google.gwt.dom.client.Touch;
 import com.google.gwt.event.shared.EventHandler;
@@ -29,6 +30,53 @@
     extends HumanInputEvent<H> {
 
   /**
+   * Dectector for browser support for touch events.
+   */
+  private static class TouchSupportDetector {
+
+    private final boolean isSupported = detectTouchSupport();
+
+    public boolean isSupported() {
+      return isSupported;
+    }
+
+    private native boolean detectTouchSupport() /*-{
+      var elem = document.createElement('div');
+      elem.setAttribute('ontouchstart', 'return;');
+      return (typeof elem.ontouchstart) == "function";
+    }-*/;
+  }
+
+  /**
+   * Detector for browsers that do not support touch events.
+   */
+  @SuppressWarnings("unused")
+  private static class TouchSupportDetectorNo extends TouchSupportDetector {
+    @Override
+    public boolean isSupported() {
+      return false;
+    }
+  }
+
+  /**
+   * The implementation singleton.
+   */
+  private static TouchSupportDetector impl;
+
+  /**
+   * Runtime check for whether touch scrolling is supported in this browser. Returns true if touch
+   * events are supported but touch based scrolling is not natively supported.
+   * 
+   * @return true if touch events are supported, false if not
+   */
+  public static boolean isSupported() {
+    if (impl == null) {
+      impl = GWT.create(TouchSupportDetector.class);
+    }
+    return impl.isSupported();
+  }
+
+  /**
    * Get an array of {@link Touch touches} which have changed since the last
    * touch event fired. Note, that for {@link TouchEndEvent touch end events},
    * the touch which has just ended will not be present in the array. Moreover,
diff --git a/user/src/com/google/gwt/touch/Touch.gwt.xml b/user/src/com/google/gwt/touch/Touch.gwt.xml
index 959061d..4e27ae4 100644
--- a/user/src/com/google/gwt/touch/Touch.gwt.xml
+++ b/user/src/com/google/gwt/touch/Touch.gwt.xml
@@ -13,25 +13,5 @@
 <!-- limitations under the License.                                         -->
 <module>
   <inherits name="com.google.gwt.user.User" />
-
-  <!-- Define the support property -->
-  <define-property name="touchEventSupport" values="maybe,no" />
-
-  <!--
-    Modern browsers either support touch events or will probably add touch
-    support in the future.
-  -->
-  <set-property name="touchEventSupport" value="maybe" />
-
-  <!-- Older browsers do not support touch events. -->
-  <set-property name="touchEventSupport" value="no">
-    <any>
-      <when-property-is name="user.agent" value="ie6" />
-    </any>
-  </set-property>
-
-  <replace-with class="com.google.gwt.touch.client.TouchScroller.TouchSupportDetectorNo">
-    <when-type-is class="com.google.gwt.touch.client.TouchScroller.TouchSupportDetector" />
-    <when-property-is name="touchEventSupport" value="no" />
-  </replace-with>
+  <source path="client"/>
 </module>
diff --git a/user/src/com/google/gwt/touch/client/TouchScroller.java b/user/src/com/google/gwt/touch/client/TouchScroller.java
index fd9967a..5c05a36 100644
--- a/user/src/com/google/gwt/touch/client/TouchScroller.java
+++ b/user/src/com/google/gwt/touch/client/TouchScroller.java
@@ -16,7 +16,6 @@
 package com.google.gwt.touch.client;
 
 import com.google.gwt.core.client.Duration;
-import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.JsArray;
 import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.core.client.Scheduler.RepeatingCommand;
@@ -43,6 +42,11 @@
 
 /**
  * Adds touch based scrolling to a scroll panel.
+ * 
+ * <p>
+ * Touch based scrolling is only supported on devices that support touch events
+ * and do not implement native touch based scrolling.
+ * </p>
  */
 @PartialSupport
 public class TouchScroller {
@@ -138,35 +142,6 @@
   }
 
   /**
-   * Dectector for browser support for touch events.
-   */
-  private static class TouchSupportDetector {
-
-    private final boolean isSupported = detectTouchSupport();
-
-    public boolean isSupported() {
-      return isSupported;
-    }
-
-    private native boolean detectTouchSupport() /*-{
-      var elem = document.createElement('div');
-      elem.setAttribute('ontouchstart', 'return;');
-      return (typeof elem.ontouchstart) == "function";
-    }-*/;
-  }
-
-  /**
-   * Detector for browsers that do not support touch events.
-   */
-  @SuppressWarnings("unused")
-  private static class TouchSupportDetectorNo extends TouchSupportDetector {
-    @Override
-    public boolean isSupported() {
-      return false;
-    }
-  }
-
-  /**
    * The number of frames per second the animation should run at.
    */
   private static final double FRAMES_PER_SECOND = 60;
@@ -193,9 +168,10 @@
   private static final int MS_PER_FRAME = (int) (1000 / FRAMES_PER_SECOND);
 
   /**
-   * The implementation singleton.
+   * A cached boolean indicating whether or not touch scrolling is supported.
+   * Set to a non-null value the first time {@link #isSupported()} is called.
    */
-  private static TouchSupportDetector impl;
+  private static Boolean isSupported;
 
   /**
    * Return a new {@link TouchScroller}.
@@ -222,25 +198,35 @@
   }
 
   /**
-   * Runtime check for whether touch events are supported in this browser.
+   * Runtime check for whether touch scrolling is supported in this browser.
+   * Returns true if touch events are supported but touch based scrolling is not
+   * natively supported.
    * 
-   * @return true if touch events are is supported, false it not
+   * @return true if touch scrolling is supported, false if not
    */
   public static boolean isSupported() {
-    return impl().isSupported();
+    if (isSupported == null) {
+      /*
+       * Android 3.0 devices support touch scrolling natively.
+       * 
+       * TODO(jlabanca): Find a more reliable way to detect if native touch
+       * scrolling is supported.
+       */
+      isSupported = TouchEvent.isSupported() && !isAndroid3();
+    }
+    return isSupported;
   }
 
   /**
-   * Get the implementation of this widget.
+   * Check if the user agent is android 3.0 or greater.
    * 
-   * @return the implementation
+   * @return true if android 3.0+
+   * 
    */
-  private static TouchSupportDetector impl() {
-    if (impl == null) {
-      impl = GWT.create(TouchSupportDetector.class);
-    }
-    return impl;
-  }
+  private static native boolean isAndroid3() /*-{
+    var ua = navigator.userAgent.toLowerCase();
+    return /android ([3-9]+)\.([0-9]+)/.exec(ua) != null;
+  }-*/;
 
   /**
    * The registration for the preview handler used to bust click events.
diff --git a/user/test/com/google/gwt/touch/client/TouchScrollTest.java b/user/test/com/google/gwt/touch/client/TouchScrollTest.java
index 6b07f39..bcf035a 100644
--- a/user/test/com/google/gwt/touch/client/TouchScrollTest.java
+++ b/user/test/com/google/gwt/touch/client/TouchScrollTest.java
@@ -257,7 +257,7 @@
   public void testCreateIfSupported() {
     // createIfSupported()
     TouchScroller scroller = TouchScroller.createIfSupported();
-    if (isTouchSupported()) {
+    if (TouchScroller.isSupported()) {
       assertNotNull("TouchScroll not created, but touch is supported", scroller);
       assertNull(scroller.getTargetWidget());
 
@@ -268,7 +268,7 @@
     // createIfSupported(HasScrolling)
     HasScrolling target = new ScrollPanel();
     scroller = TouchScroller.createIfSupported(target);
-    if (isTouchSupported()) {
+    if (TouchScroller.isSupported()) {
       assertNotNull("TouchScroll not created, but touch is supported", scroller);
       assertEquals(target, scroller.getTargetWidget());
 
@@ -511,10 +511,4 @@
     scrollPanel = null;
     scroller = null;
   }
-
-  private native boolean isTouchSupported() /*-{
-    var elem = document.createElement('div');
-    elem.setAttribute('ontouchstart', 'return;');
-    return (typeof elem.ontouchstart) == "function";
-  }-*/;
 }