• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package io.flutter.embedding.android;
6 
7 import android.support.annotation.NonNull;
8 import android.support.annotation.Nullable;
9 import android.view.KeyCharacterMap;
10 import android.view.KeyEvent;
11 
12 import io.flutter.embedding.engine.systemchannels.KeyEventChannel;
13 import io.flutter.plugin.editing.TextInputPlugin;
14 
15 public class AndroidKeyProcessor {
16   @NonNull
17   private final KeyEventChannel keyEventChannel;
18   @NonNull
19   private final TextInputPlugin textInputPlugin;
20   private int combiningCharacter;
21 
AndroidKeyProcessor(@onNull KeyEventChannel keyEventChannel, @NonNull TextInputPlugin textInputPlugin)22   public AndroidKeyProcessor(@NonNull KeyEventChannel keyEventChannel, @NonNull TextInputPlugin textInputPlugin) {
23     this.keyEventChannel = keyEventChannel;
24     this.textInputPlugin = textInputPlugin;
25   }
26 
onKeyUp(@onNull KeyEvent keyEvent)27   public void onKeyUp(@NonNull KeyEvent keyEvent) {
28     Character complexCharacter = applyCombiningCharacterToBaseCharacter(keyEvent.getUnicodeChar());
29     keyEventChannel.keyUp(
30         new KeyEventChannel.FlutterKeyEvent(keyEvent, complexCharacter)
31     );
32   }
33 
onKeyDown(@onNull KeyEvent keyEvent)34   public void onKeyDown(@NonNull KeyEvent keyEvent) {
35     if (textInputPlugin.getLastInputConnection() != null
36         && textInputPlugin.getInputMethodManager().isAcceptingText()) {
37       textInputPlugin.getLastInputConnection().sendKeyEvent(keyEvent);
38     }
39 
40     Character complexCharacter = applyCombiningCharacterToBaseCharacter(keyEvent.getUnicodeChar());
41     keyEventChannel.keyDown(
42         new KeyEventChannel.FlutterKeyEvent(keyEvent, complexCharacter)
43     );
44   }
45 
46   /**
47    * Applies the given Unicode character in {@code newCharacterCodePoint} to a previously
48    * entered Unicode combining character and returns the combination of these characters
49    * if a combination exists.
50    * <p>
51    * This method mutates {@link #combiningCharacter} over time to combine characters.
52    * <p>
53    * One of the following things happens in this method:
54    * <ul>
55    *   <li>If no previous {@link #combiningCharacter} exists and the {@code newCharacterCodePoint}
56    *   is not a combining character, then {@code newCharacterCodePoint} is returned.</li>
57    *   <li>If no previous {@link #combiningCharacter} exists and the {@code newCharacterCodePoint}
58    *   is a combining character, then {@code newCharacterCodePoint} is saved as the
59    *   {@link #combiningCharacter} and null is returned.</li>
60    *   <li>If a previous {@link #combiningCharacter} exists and the {@code newCharacterCodePoint}
61    *   is also a combining character, then the {@code newCharacterCodePoint} is combined with
62    *   the existing {@link #combiningCharacter} and null is returned.</li>
63    *   <li>If a previous {@link #combiningCharacter} exists and the {@code newCharacterCodePoint}
64    *    is not a combining character, then the {@link #combiningCharacter} is applied to the
65    *    regular {@code newCharacterCodePoint} and the resulting complex character is returned. The
66    *    {@link #combiningCharacter} is cleared.</li>
67    * </ul>
68    * <p>
69    * The following reference explains the concept of a "combining character":
70    * https://en.wikipedia.org/wiki/Combining_character
71    */
72   @Nullable
applyCombiningCharacterToBaseCharacter(int newCharacterCodePoint)73   private Character applyCombiningCharacterToBaseCharacter(int newCharacterCodePoint) {
74     if (newCharacterCodePoint == 0) {
75       return null;
76     }
77 
78     Character complexCharacter = (char) newCharacterCodePoint;
79     boolean isNewCodePointACombiningCharacter = (newCharacterCodePoint & KeyCharacterMap.COMBINING_ACCENT) != 0;
80     if (isNewCodePointACombiningCharacter) {
81       // If a combining character was entered before, combine this one with that one.
82       int plainCodePoint = newCharacterCodePoint & KeyCharacterMap.COMBINING_ACCENT_MASK;
83       if (combiningCharacter != 0) {
84         combiningCharacter = KeyCharacterMap.getDeadChar(combiningCharacter, plainCodePoint);
85       } else {
86         combiningCharacter = plainCodePoint;
87       }
88     } else {
89       // The new character is a regular character. Apply combiningCharacter to it, if it exists.
90       if (combiningCharacter != 0) {
91         int combinedChar = KeyCharacterMap.getDeadChar(combiningCharacter, newCharacterCodePoint);
92         if (combinedChar > 0) {
93           complexCharacter = (char) combinedChar;
94         }
95         combiningCharacter = 0;
96       }
97     }
98 
99     return complexCharacter;
100   }
101 }
102