Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8270529

Inaccurate KeyTyped value when scanning a barcode with 'GS' character

XMLWordPrintable

    • generic
    • generic

      ADDITIONAL SYSTEM INFORMATION :
      OS - Windows 10, Java - 8 and 11

      A DESCRIPTION OF THE PROBLEM :
      When barcode scanner input is listened as KeyEvent, if the barcode contains 'GS' character in some cases, it produce incorrect value for KeyEvent.getKeyChar() for KeyTyped of 'GS' character. 29 in the the keyChar value for 'GS' but in some cases it produce random values.

      Easiest way to reproduce is use a look and feel like Nimbus or add tiny delay like 2 millisecond for KeyTyped listener if it is plain swing.

      'GS' character is type by following key type sequence from number pad with 'Alt'. 0 twice then 2 then 9.
      Even when KeyType produce incorrect value Keypress and Release contains this correct sequence.

      This can not be reproduce when 'GS' is manually typed or by robot. It doesn't reproduce with other native apps such as NotePad++

      Since 'GS' character is used as delimiter in GS1 2D barcodes this makes it difficult to parse barcode content accurately



      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run SwingApp(without any look and feel) or NimbusApp(with Nimbus look and feel) in given sample. Focus on the text field on the top
      1. Scan barcode with 'GS' character -> ex:- https://barcode.tec-it.com/en/GS1DataMatrix?data=1%5CF23 or https://barcode.tec-it.com/en/GS1DataMatrix?data=1%5CF23%5CF111

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Text area in the bottom should have lines which contains "GS1 on position = <<index of the GS character>>'
      For https://barcode.tec-it.com/en/GS1DataMatrix?data=1%5CF23 - "GS1 on position = 1"
      for https://barcode.tec-it.com/en/GS1DataMatrix?data=1%5CF23%5CF111 - "GS1 on position = 1\nGS1 on position = 4"

      ACTUAL -
      In most cases Java doesn't produce correct keyType event for 'GS' with keyChar 29 to produce correct results. But for SwingApp if delay is disabled or increased it is very unlikely to reproduce

      ---------- BEGIN SOURCE ----------
      import java.awt.AWTException;
      import java.awt.Robot;
      import java.awt.event.KeyAdapter;
      import java.awt.event.KeyEvent;

      import javax.swing.JButton;
      import javax.swing.JFrame;
      import javax.swing.JLabel;
      import javax.swing.JOptionPane;
      import javax.swing.JTextArea;
      import javax.swing.JTextField;
      import javax.swing.WindowConstants;

      public class ScannerWindow
      {

        private int i;
        private final JFrame frame;

        public ScannerWindow()
        {
          this(false, 0);
        }

        /**
         * In some cases such as plain swing app can reproduce this bug only when there is delay between event every key type
         *
         * @param forceDelay introduce delay to key listener
         * @param delay delay in milliseconds
         */
        public ScannerWindow(boolean forceDelay, int delay)
        {
          frame = new JFrame();
          frame.setLayout(null);

          JTextArea ta = new JTextArea();

          JLabel lblScan = new JLabel("Focus here to scan");
          lblScan.setBounds(50, 20, 300, 40);
          frame.add(lblScan);

          JTextField txt = new JTextField();
          txt.setBounds(50, 80, 400, 40);
          txt.addKeyListener(new KeyAdapter()
            {
              @Override
              public void keyTyped(KeyEvent e)
              {
                if (forceDelay)
                {
                  try
                  {
                    Thread.sleep(delay);
                  }
                  catch (InterruptedException e1)
                  {
                  }
                }
                //'GS' is typed
                if (e.getKeyChar() == 29)
                {
                  ta.append("\n GS1 on position = " + i);
                }
                i++;
              }
            });
          frame.add(txt);

          JButton btnClear = new JButton("Clear");
          btnClear.addActionListener(e -> {
            ta.setText("");
            txt.setText("");
            i = 0;
          });
          btnClear.setBounds(50, 140, 100, 40);
          frame.add(btnClear);

          JButton btnRobotScan = new JButton("Robot scan");
          btnRobotScan.addActionListener(e -> {
            String input = JOptionPane.showInputDialog("Content to scan, g will be replaced by GS");
            txt.requestFocusInWindow();
            try
            {

              Robot robot = new Robot();
              for (int i = 0; i < input.length(); i++)
              {
                char c = input.charAt(i);
                if (c == 'g')
                {
                  robot.keyPress(KeyEvent.VK_ALT);
                  keyType(robot, KeyEvent.VK_NUMPAD0);
                  keyType(robot, KeyEvent.VK_NUMPAD0);
                  keyType(robot, KeyEvent.VK_NUMPAD2);
                  robot.keyPress(KeyEvent.VK_NUMPAD9);
                  robot.keyRelease(KeyEvent.VK_ALT);
                  robot.keyRelease(KeyEvent.VK_NUMPAD9);
                }
                else
                {
                  keyType(robot, (char) c);
                }
              }
              keyType(robot, KeyEvent.VK_ENTER);
            }
            catch (AWTException awtException)
            {
              awtException.printStackTrace();
            }
          });
          btnRobotScan.setBounds(180, 140, 100, 40);
          frame.add(btnRobotScan);


          ta.setBounds(50, 200, 400, 250);
          frame.add(ta);

          frame.setSize(500, 500);
          frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        }

        private void keyType(Robot robot, int c)
        {
          robot.keyPress(c);
          robot.keyRelease(c);
        }

        private void keyType(Robot robot, char c)
        {
          robot.keyPress(Character.toLowerCase(c));
          robot.keyRelease(Character.toLowerCase(c));
        }


        public void show()
        {
          frame.setVisible(true);
        }
      }

      ---Plain swing app---
      import java.awt.EventQueue;

      public class SwingApp
      {

        public static void main(String[] args)
        {
          /*
           * reproduce for 2 millisecond delay but not for delay like 100 or without delay
           */
          EventQueue.invokeLater(() -> new ScannerWindow(true, 2).show());
        }
      }

      --With Nimbus look and feel---
      import java.awt.EventQueue;

      import javax.swing.UIManager;
      import javax.swing.UIManager.LookAndFeelInfo;
      import javax.swing.UnsupportedLookAndFeelException;

      public class NimbusApp
      {

        public static void main(String[] args)
        {
          boolean set = false;
          try
          {
            for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
            {
              if ("Nimbus".equals(info.getName()))
              {
                UIManager.setLookAndFeel(info.getClassName());
                set = true;
                break;
              }
            }
          }
          catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException e)
          {
          }

          if (set)
          {
            EventQueue.invokeLater(() -> new ScannerWindow().show());
          }
        }

      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Rely on series of KeyPress, Release events

      FREQUENCY : often


        1. NimbusApp.java
          0.8 kB
          Praveen Narayanaswamy
        2. ScannerWindow.java
          3 kB
          Praveen Narayanaswamy
        3. SwingApp.java
          0.3 kB
          Praveen Narayanaswamy

            azvegint Alexander Zvegintsev
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated: