Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    recordClick event fires very early on touch devices using Phonegap

    Hi Isomorphic,


    We are developing for Android and iOS touch devices, using Phonegap to bring the app to the app markets. We use ListGrids, with recordClickHandler in our application. If one scrolls down in the example below, and touches the screen, to stop at a specific record, the attached recordClickHandler fires immediately. Is it possible, to change this sensitive behaviour so, that the recordClick does not fire if one wants just to stop at a specific position while scrolling? See below the testcase setup for Phonegap.

    Phonegap Setup:

    config.xml

    Code:
    <?xml version='1.0' encoding='utf-8'?>
    <widget id="com.your.bundle.identifier" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:gap="http://phonegap.com/ns/1.0">
        <name>helloworld</name>
        <description>
            Hello World sample application that responds to the deviceready event.
        </description>
        <author email="support@phonegap.com" href="http://phonegap.com">
            PhoneGap Team
        </author>
        <content src="index.html" />
        <preference name="DisallowOverscroll" value="true" />
        <preference name="android-minSdkVersion" value="14" />
        <platform name="android">
            <icon density="ldpi" src="www/res/icon/android/drawable-ldpi-icon.png" />
            <icon density="mdpi" src="www/res/icon/android/drawable-mdpi-icon.png" />
            <icon density="hdpi" src="www/res/icon/android/drawable-hdpi-icon.png" />
            <icon density="xhdpi" src="www/res/icon/android/drawable-xhdpi-icon.png" />
            <icon density="xxhdpi" src="www/res/icon/android/drawable-xxhdpi-icon.png" />
            <icon density="xxxhdpi" src="www/res/icon/android/drawable-xxxhdpi-icon.png" />
            <splash density="land-ldpi" src="www/res/screen/android/drawable-land-ldpi-screen.png" />
            <splash density="land-mdpi" src="www/res/screen/android/drawable-land-mdpi-screen.png" />
            <splash density="land-hdpi" src="www/res/screen/android/drawable-land-hdpi-screen.png" />
            <splash density="land-xhdpi" src="www/res/screen/android/drawable-land-xhdpi-screen.png" />
            <splash density="land-xxhdpi" src="www/res/screen/android/drawable-land-xxhdpi-screen.png" />
            <splash density="land-xxxhdpi" src="www/res/screen/android/drawable-land-xxxhdpi-screen.png" />
            <splash density="port-ldpi" src="www/res/screen/android/drawable-port-ldpi-screen.png" />
            <splash density="port-mdpi" src="www/res/screen/android/drawable-port-mdpi-screen.png" />
            <splash density="port-hdpi" src="www/res/screen/android/drawable-port-hdpi-screen.png" />
            <splash density="port-xhdpi" src="www/res/screen/android/drawable-port-xhdpi-screen.png" />
            <splash density="port-xxhdpi" src="www/res/screen/android/drawable-port-xxhdpi-screen.png" />
            <splash density="port-xxxhdpi" src="www/res/screen/android/drawable-port-xxxhdpi-screen.png" />
        </platform>
        <platform name="ios">
            <icon height="57" platform="ios" src="www/res/icon/ios/icon.png" width="57" />
            <icon height="114" platform="ios" src="www/res/icon/ios/icon@2x.png" width="114" />
            <icon height="40" platform="ios" src="www/res/icon/ios/icon-40.png" width="40" />
            <icon height="80" platform="ios" src="www/res/icon/ios/icon-40@2x.png" width="80" />
            <icon height="50" platform="ios" src="www/res/icon/ios/icon-50.png" width="50" />
            <icon height="100" platform="ios" src="www/res/icon/ios/icon-50@2x.png" width="100" />
            <icon height="60" platform="ios" src="www/res/icon/ios/icon-60.png" width="60" />
            <icon height="120" platform="ios" src="www/res/icon/ios/icon-60@2x.png" width="120" />
            <icon height="180" platform="ios" src="www/res/icon/ios/icon-60@3x.png" width="180" />
            <icon height="72" platform="ios" src="www/res/icon/ios/icon-72.png" width="72" />
            <icon height="144" platform="ios" src="www/res/icon/ios/icon-72@2x.png" width="144" />
            <icon height="76" platform="ios" src="www/res/icon/ios/icon-76.png" width="76" />
            <icon height="152" platform="ios" src="www/res/icon/ios/icon-76@2x.png" width="152" />
            <icon height="29" platform="ios" src="www/res/icon/ios/icon-small.png" width="29" />
            <icon height="58" platform="ios" src="www/res/icon/ios/icon-small@2x.png" width="58" />
            <icon height="87" platform="ios" src="www/res/icon/ios/icon-small@3x.png" width="87" />
            <splash height="1136" platform="ios" src="www/res/screen/ios/Default-568h@2x~iphone.png" width="640" />
            <splash height="1334" platform="ios" src="www/res/screen/ios/Default-667h.png" width="750" />
            <splash height="2208" platform="ios" src="www/res/screen/ios/Default-736h.png" width="1242" />
            <splash height="1242" platform="ios" src="www/res/screen/ios/Default-Landscape-736h.png" width="2208" />
            <splash height="1536" platform="ios" src="www/res/screen/ios/Default-Landscape@2x~ipad.png" width="2048" />
            <splash height="768" platform="ios" src="www/res/screen/ios/Default-Landscape~ipad.png" width="1024" />
            <splash height="2048" platform="ios" src="www/res/screen/ios/Default-Portrait@2x~ipad.png" width="1536" />
            <splash height="1024" platform="ios" src="www/res/screen/ios/Default-Portrait~ipad.png" width="768" />
            <splash height="960" platform="ios" src="www/res/screen/ios/Default@2x~iphone.png" width="640" />
            <splash height="480" platform="ios" src="www/res/screen/ios/Default~iphone.png" width="320" />
        </platform>
        <platform name="wp8">
            <icon height="99" platform="wp8" src="www/res/icon/wp8/ApplicationIcon.png" width="99" />
            <icon height="159" platform="wp8" src="www/res/icon/wp8/Background.png" width="159" />
            <splash height="1280" platform="wp8" src="www/res/screen/wp8/screen-portrait.jpg" width="768" />
        </platform>
        <platform name="windows">
            <icon height="150" platform="windows" src="www/res/icon/windows/Square150x150Logo.scale-100.png" width="150" />
            <icon height="30" platform="windows" src="www/res/icon/windows/Square30x30Logo.scale-100.png" width="30" />
            <icon height="50" platform="windows" src="www/res/icon/windows/StoreLogo.scale-100.png" width="50" />
            <splash height="300" platform="windows" src="www/res/screen/windows/SplashScreen.scale-100.png" width="620" />
            <icon height="120" platform="windows" src="www/res/icon/windows/StoreLogo.scale-240.png" width="120" />
            <icon height="44" platform="windows" src="www/res/icon/windows/Square44x44Logo.scale-100.png" width="44" />
            <icon height="106" platform="windows" src="www/res/icon/windows/Square44x44Logo.scale-240.png" width="106" />
            <icon height="70" platform="windows" src="www/res/icon/windows/Square70x70Logo.scale-100.png" width="70" />
            <icon height="71" platform="windows" src="www/res/icon/windows/Square71x71Logo.scale-100.png" width="71" />
            <icon height="170" platform="windows" src="www/res/icon/windows/Square71x71Logo.scale-240.png" width="170" />
            <icon height="360" platform="windows" src="www/res/icon/windows/Square150x150Logo.scale-240.png" width="360" />
            <icon height="310" platform="windows" src="www/res/icon/windows/Square310x310Logo.scale-100.png" width="310" />
            <icon height="150" platform="windows" src="www/res/icon/windows/Wide310x150Logo.scale-100.png" width="310" />
            <icon height="360" platform="windows" src="www/res/icon/windows/Wide310x150Logo.scale-240.png" width="744" />
            <splash height="1920" platform="windows" src="www/res/screen/windows/SplashScreenPhone.scale-240.png" width="1152" />
        </platform>
        <access origin="*" />
        <allow-intent href="http://*/*" />
        <allow-intent href="https://*/*" />
        <allow-intent href="tel:*" />
        <allow-intent href="sms:*" />
        <allow-intent href="mailto:*" />
        <allow-intent href="geo:*" />
        <platform name="android">
            <allow-intent href="market:*" />
        </platform>
        <platform name="ios">
            <allow-intent href="itms:*" />
            <allow-intent href="itms-apps:*" />
        </platform>
        <allow-navigation href="*" />
        <allow-navigation href="*" />
        <allow-navigation href="http://*/*" />
        <allow-navigation href="https://*/*" />
        <allow-navigation href="tel:*" />
        <allow-navigation href="sms:*" />
        <allow-navigation href="mailto:*" />
        <allow-navigation href="geo:*" />
        <engine name="ios" spec="~4.4.0" />
        <plugin name="cordova-plugin-battery-status" spec="~1.2.4" />
        <plugin name="cordova-plugin-camera" spec="~2.4.1" />
        <plugin name="cordova-plugin-device" spec="~1.1.6" />
        <plugin name="cordova-plugin-dialogs" spec="~1.3.3" />
        <plugin name="cordova-plugin-file" spec="~4.3.3" />
        <plugin name="cordova-plugin-geolocation" spec="~2.4.3" />
        <plugin name="cordova-plugin-globalization" spec="~1.0.7" />
        <plugin name="cordova-plugin-inappbrowser" spec="~1.7.1" />
        <plugin name="cordova-plugin-media-capture" spec="~1.4.3" />
        <plugin name="cordova-plugin-network-information" spec="~1.3.3" />
        <plugin name="cordova-plugin-vibration" spec="~2.1.5" />
    </widget>
    index.html

    Code:
    <!DOCTYPE html>
    <!--
        Copyright (c) 2012-2016 Adobe Systems Incorporated. All rights reserved.
    
        Licensed to the Apache Software Foundation (ASF) under one
        or more contributor license agreements.  See the NOTICE file
        distributed with this work for additional information
        regarding copyright ownership.  The ASF licenses this file
        to you 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.
    -->
    <html>
    
    <head>
        <meta charset="utf-8" />
        <meta name="format-detection" content="telephone=no" />
        <meta name="msapplication-tap-highlight" content="no" />
        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width" />
        <!-- This is a wide open CSP declaration. To lock this down for production, see below. -->
        <meta http-equiv="Content-Security-Policy" content="default-src * 'unsafe-inline' gap:; style-src 'self' 'unsafe-inline'; media-src *" />
        <!-- Good default declaration:
        * gap: is required only on iOS (when using UIWebView) and is needed for JS->native communication
        * https://ssl.gstatic.com is required only on Android and is needed for TalkBack to function properly
        * Disables use of eval() and inline scripts in order to mitigate risk of XSS vulnerabilities. To change this:
            * Enable inline JS: add 'unsafe-inline' to default-src
            * Enable eval(): add 'unsafe-eval' to default-src
        * Create your own at http://cspisawesome.com
        -->
        <!-- <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: 'unsafe-inline' https://ssl.gstatic.com; style-src 'self' 'unsafe-inline'; media-src *" /> -->
    
        <link rel="stylesheet" type="text/css" href="css/index.css" />
        <title>Hello World</title>
    </head>
    
    <body>
        <div class="app">
            <h1>PhoneGap</h1>
            <div id="deviceready" class="blink">
                <p class="event listening">Connecting to Device</p>
                <p class="event received">Device is Ready</p>
            </div>
        </div>
        <script type="text/javascript" src="cordova.js"></script>
        <script type="text/javascript" src="js/index.js"></script>
        <script type="text/javascript">
            app.initialize();
    
      window.location.href = "http://192.168.27.186:8888/BuiltInDS.html";
        </script>
    </body>
    
    </html>
    The project was created and built with Phonegap CLI v. 7.0.1 and run with Xcode10 on Mac (macOS High Sierra Version 10.13.6)

    SmartGWT Setup:

    Code:
    package com.smartgwt.sample.client;
    
    import com.google.gwt.core.client.EntryPoint;
    import com.smartgwt.client.core.KeyIdentifier;
    import com.smartgwt.client.data.DataSource;
    import com.smartgwt.client.data.Record;
    import com.smartgwt.client.types.SelectionStyle;
    import com.smartgwt.client.types.SortArrow;
    import com.smartgwt.client.util.Page;
    import com.smartgwt.client.util.PageKeyHandler;
    import com.smartgwt.client.util.SC;
    import com.smartgwt.client.widgets.IButton;
    import com.smartgwt.client.widgets.form.DynamicForm;
    import com.smartgwt.client.widgets.grid.ListGrid;
    import com.smartgwt.client.widgets.grid.ListGridField;
    import com.smartgwt.client.widgets.grid.ListGridRecord;
    import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
    import com.smartgwt.client.widgets.grid.events.RecordClickHandler;
    import com.smartgwt.client.widgets.layout.VStack;
    import com.smartgwt.client.widgets.viewer.DetailViewer;
    
    /**
     * Entry point classes define <code>onModuleLoad()</code>.
     */
    public class BuiltInDS implements EntryPoint {
        private ListGrid boundList;
        private DynamicForm boundForm;
        private IButton saveBtn;
        private DetailViewer boundViewer;
        private IButton newBtn;
    
        /**
         * This is the entry point method.
         */
        public void onModuleLoad() {
            KeyIdentifier debugKey = new KeyIdentifier();
            debugKey.setCtrlKey(true);
            debugKey.setKeyName("D");
    
            Page.registerKey(debugKey, new PageKeyHandler() {
                public void execute(String keyName) {
                    SC.showConsole();
                }
            });
    
            ListGrid grid = new ListGrid();
            // grid.setLeft(20);
            // grid.setTop(75);
            grid.setWidth100();
            grid.setHeight("20%");
            grid.setLeaveScrollbarGap(false);
            grid.setShowSortArrow(SortArrow.NONE);
            grid.setCanSort(false);
            grid.setFields(new ListGridField("dsTitle", "Select a DataSource"));
            grid.setData(new ListGridRecord[] { new DSRecord("Animals", "animals"), new DSRecord("Office Supplies", "supplyItem"),
                    new DSRecord("Employees", "employees") });
            grid.setSelectionType(SelectionStyle.SINGLE);
            grid.addRecordClickHandler(new RecordClickHandler() {
                public void onRecordClick(RecordClickEvent event) {
                    DSRecord record = (DSRecord) event.getRecord();
                    bindComponents(record.getDsName());
    
                }
            });
    
            grid.draw();
    
            VStack vStack = new VStack();
            // vStack.setLeft(175);
            // vStack.setTop(75);
            vStack.setWidth100();
            vStack.setHeight100();
            vStack.setMembersMargin(20);
    
            boundList = new ListGrid();
            boundList.setHeight100();
            boundList.setCanEdit(true);
            boundList.setUseTouchScrolling(true);
            boundList.setAlwaysShowScrollbars(true);
            boundList.setShowCustomScrollbars(true);
            boundList.addRecordClickHandler(new RecordClickHandler() {
                public void onRecordClick(RecordClickEvent event) {
                    Record record = event.getRecord();
                    boundForm.editRecord(record);
    
                    boundViewer.viewSelectedData(boundList);
                    SC.say("Hello");
                }
            });
            vStack.addMember(grid);
            vStack.addMember(boundList);
    
            boundForm = new DynamicForm();
            boundForm.setNumCols(6);
            boundForm.setAutoFocus(false);
    
            boundViewer = new DetailViewer();
            vStack.addMember(boundViewer);
    
            vStack.draw();
        }
    
        private void bindComponents(String dsName) {
            DataSource ds = DataSource.get(dsName);
            boundList.setDataSource(ds);
            boundViewer.setDataSource(ds);
            boundForm.setDataSource(ds);
            boundList.fetchData();
            newBtn.enable();
            saveBtn.disable();
        }
    }
    Setup:
    SmartClient Version: v12.0p_2018-10-09/PowerEdition Deployment (built 2018-10-09)
    Tested Devices: iPhone 6s Plus with iOS12

    Kind Regards

    #2
    In the original thread where you reported this, you said it affected some Android devices. Does this reproduce on PhoneGap for Android, or only on iOS? It looks like you specifically stated that the other issue (involving scrollbars) did not reproduce on PhoneGap for Android.

    Comment


      #3
      Yes, the "recordClick" issue affects both PhoneGap for iOS and PhoneGap for Android! The issue (involving scrollbars) affects only PhoneGap for iOS!

      Comment


        #4
        We are able to reproduce this and are looking into it.

        Comment


          #5
          First, please note that both issues you've reported with PhoneGap are really bugs arising from the WebView layer. We'll discuss them both here and link the other thread to this one to avoid duplication.

          Apple has reportedly already deprecated UIWebView in iOS 12, and it may be removed in iOS 13 according to this, so our recommendation is for you to update your PhoneGap project to use WKWebView, which is now an officially supported plugin of PhoneGap. The GitHub page and install instructions are here.

          WKWebView is an Apple API, so for Android our recommendation is to report the behavior to Google as bugs so that it may be potentially fixed in the future. There are some unofficial solutions here such as the "Crosstalk" plugin, but it's up to you whether they are appropriate.

          Now, let's consider whether the Framework can work around WebView's bugs:
          • In the case of the scrollbar thumb "jumping," we don't receive any events during a momentum scroll after the touchEnd, until it completes with an onscroll event. That final onscroll may be up to 3 seconds after the touchEnd. Moreover, during that time, the DOM is frozen and the element's scrollTop isn't updated. So there's no way for the Framework to detect that a momentum scroll is in progress, and thus no way for the Framework to correct the jump. (So your best bet is to upgrade your iOS PhoneGap project to use the WKWebView plugin as mentioned above.)
          • In the case of tap-to-stop generating an unwanted click, the Framework normally doesn't receive this click so doesn't have to deal with it - that's true for iOS mobile Safari and Android Chrome. In WebView, we receive a touchStart/touchEnd for tap-to-stop so we have to use the onscroll event to attempt to recognize and block the unwanted click. Sometimes we receive the onscroll event after the touchStart and before the touchEnd, so we've added logic to our EventHandler to cancel the click in this situation.
               However, while testing various devices, including simulators, we've seen that you don't always get an onscroll event before the touchEnd, which means our workaround may not always prevent the unwanted click. However, on a fast device it may work - you'll have to check. The only way to prevent the unwanted click 100% would be to delay every click on a scrollable element for up to 500ms to wait for the final onscroll event, which would cancel the click. However, this would have a very negative impact on usability, so we've chosen not to pursue such an approach.

          The workaround we have added will be applied to SGWT 6.1p/SC 11.p and newer releases, and will be in the nightly builds dated 2018-10-25 and beyond.

          Comment

          Working...
          X