| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.chromoting; | 5 package org.chromium.chromoting; |
| 6 | 6 |
| 7 import android.annotation.SuppressLint; | 7 import android.annotation.SuppressLint; |
| 8 import android.app.Activity; | 8 import android.app.Activity; |
| 9 import android.content.res.Configuration; | 9 import android.content.res.Configuration; |
| 10 import android.os.Build; | 10 import android.os.Build; |
| 11 import android.os.Bundle; | 11 import android.os.Bundle; |
| 12 import android.view.KeyCharacterMap; |
| 12 import android.view.KeyEvent; | 13 import android.view.KeyEvent; |
| 13 import android.view.Menu; | 14 import android.view.Menu; |
| 14 import android.view.MenuItem; | 15 import android.view.MenuItem; |
| 15 import android.view.View; | 16 import android.view.View; |
| 16 import android.view.inputmethod.InputMethodManager; | 17 import android.view.inputmethod.InputMethodManager; |
| 17 import android.widget.ImageButton; | 18 import android.widget.ImageButton; |
| 18 | 19 |
| 19 import org.chromium.chromoting.jni.JniInterface; | 20 import org.chromium.chromoting.jni.JniInterface; |
| 20 | 21 |
| 22 import java.util.Set; |
| 23 import java.util.TreeSet; |
| 24 |
| 21 /** | 25 /** |
| 22 * A simple screen that does nothing except display a DesktopView and notify it
of rotations. | 26 * A simple screen that does nothing except display a DesktopView and notify it
of rotations. |
| 23 */ | 27 */ |
| 24 public class Desktop extends Activity implements View.OnSystemUiVisibilityChange
Listener { | 28 public class Desktop extends Activity implements View.OnSystemUiVisibilityChange
Listener { |
| 25 /** Web page to be displayed in the Help screen when launched from this acti
vity. */ | 29 /** Web page to be displayed in the Help screen when launched from this acti
vity. */ |
| 26 private static final String HELP_URL = | 30 private static final String HELP_URL = |
| 27 "http://support.google.com/chrome/?p=mobile_crd_connecthost"; | 31 "http://support.google.com/chrome/?p=mobile_crd_connecthost"; |
| 28 | 32 |
| 29 /** The surface that displays the remote host's desktop feed. */ | 33 /** The surface that displays the remote host's desktop feed. */ |
| 30 private DesktopView mRemoteHostDesktop; | 34 private DesktopView mRemoteHostDesktop; |
| 31 | 35 |
| 32 /** The button used to show the action bar. */ | 36 /** The button used to show the action bar. */ |
| 33 private ImageButton mOverlayButton; | 37 private ImageButton mOverlayButton; |
| 34 | 38 |
| 39 /** Set of pressed keys for which we've sent TextEvent. */ |
| 40 private Set<Integer> mPressedTextKeys = new TreeSet<Integer>(); |
| 41 |
| 35 /** Called when the activity is first created. */ | 42 /** Called when the activity is first created. */ |
| 36 @Override | 43 @Override |
| 37 public void onCreate(Bundle savedInstanceState) { | 44 public void onCreate(Bundle savedInstanceState) { |
| 38 super.onCreate(savedInstanceState); | 45 super.onCreate(savedInstanceState); |
| 39 setContentView(R.layout.desktop); | 46 setContentView(R.layout.desktop); |
| 40 mRemoteHostDesktop = (DesktopView)findViewById(R.id.desktop_view); | 47 mRemoteHostDesktop = (DesktopView)findViewById(R.id.desktop_view); |
| 41 mOverlayButton = (ImageButton)findViewById(R.id.desktop_overlay_button); | 48 mOverlayButton = (ImageButton)findViewById(R.id.desktop_overlay_button); |
| 42 mRemoteHostDesktop.setDesktop(this); | 49 mRemoteHostDesktop.setDesktop(this); |
| 43 | 50 |
| 44 // Ensure the button is initially hidden. | 51 // Ensure the button is initially hidden. |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 169 return super.onOptionsItemSelected(item); | 176 return super.onOptionsItemSelected(item); |
| 170 } | 177 } |
| 171 | 178 |
| 172 /** | 179 /** |
| 173 * Called once when a keyboard key is pressed, then again when that same key
is released. This | 180 * Called once when a keyboard key is pressed, then again when that same key
is released. This |
| 174 * is not guaranteed to be notified of all soft keyboard events: certian key
boards might not | 181 * is not guaranteed to be notified of all soft keyboard events: certian key
boards might not |
| 175 * call it at all, while others might skip it in certain situations (e.g. sw
ipe input). | 182 * call it at all, while others might skip it in certain situations (e.g. sw
ipe input). |
| 176 */ | 183 */ |
| 177 @Override | 184 @Override |
| 178 public boolean dispatchKeyEvent(KeyEvent event) { | 185 public boolean dispatchKeyEvent(KeyEvent event) { |
| 179 // Send ACTION_MULTIPLE event as TextEvent. | 186 // Send TextEvent in two cases: |
| 180 // | 187 // 1. This is an ACTION_MULTIPLE event. |
| 181 // TODO(sergeyu): For all keys on English keyboard Android generates | 188 // 2. The event was generated by on-screen keyboard and Ctrl, Alt and |
| 182 // ACTION_DOWN/ACTION_UP events, so they are sent as KeyEvent instead of | 189 // Meta are not pressed. |
| 183 // TextEvent. As result the host may handle them as non-English chars | 190 // This ensures that on-screen keyboard always injects input that |
| 184 // when it has non-English layout selected, which might be confusing for | 191 // correspond to what user sees on the screen, while physical keyboard |
| 185 // the user. This code should be fixed to send all text input events as | 192 // acts as if it is connected to the remote host. |
| 186 // TextEvent, but it cannot be done now because not all hosts support | |
| 187 // TextEvent. Also, to handle keyboard shortcuts properly this code will | |
| 188 // need to track the state of modifier keys (such as Ctrl or Alt) and | |
| 189 // send KeyEvents in the case any of the modifier keys are pressed. | |
| 190 if (event.getAction() == KeyEvent.ACTION_MULTIPLE) { | 193 if (event.getAction() == KeyEvent.ACTION_MULTIPLE) { |
| 191 JniInterface.sendTextEvent(event.getCharacters()); | 194 JniInterface.sendTextEvent(event.getCharacters()); |
| 192 return super.dispatchKeyEvent(event); | 195 return super.dispatchKeyEvent(event); |
| 193 } | 196 } |
| 194 | 197 |
| 195 boolean depressed = event.getAction() == KeyEvent.ACTION_DOWN; | 198 int keyCode = event.getKeyCode(); |
| 199 boolean pressed = event.getAction() == KeyEvent.ACTION_DOWN; |
| 196 | 200 |
| 197 switch (event.getKeyCode()) { | 201 // For Enter getUnicodeChar() returns 10 (line feed), but we still |
| 202 // want to send it as KeyEvent. |
| 203 int unicode = keyCode != KeyEvent.KEYCODE_ENTER ? event.getUnicodeChar()
: 0; |
| 204 |
| 205 boolean no_modifiers = !event.isAltPressed() && |
| 206 !event.isCtrlPressed() && !event.isMetaPressed(); |
| 207 |
| 208 if (event.getDeviceId() == KeyCharacterMap.VIRTUAL_KEYBOARD && |
| 209 pressed && unicode != 0 && no_modifiers) { |
| 210 mPressedTextKeys.add(keyCode); |
| 211 int[] codePoints = { unicode }; |
| 212 JniInterface.sendTextEvent(new String(codePoints, 0, 1)); |
| 213 return super.dispatchKeyEvent(event); |
| 214 } |
| 215 |
| 216 if (!pressed && mPressedTextKeys.contains(keyCode)) { |
| 217 mPressedTextKeys.remove(keyCode); |
| 218 return super.dispatchKeyEvent(event); |
| 219 } |
| 220 |
| 221 switch (keyCode) { |
| 198 case KeyEvent.KEYCODE_AT: | 222 case KeyEvent.KEYCODE_AT: |
| 199 JniInterface.sendKeyEvent(KeyEvent.KEYCODE_SHIFT_LEFT, depressed
); | 223 JniInterface.sendKeyEvent(KeyEvent.KEYCODE_SHIFT_LEFT, pressed); |
| 200 JniInterface.sendKeyEvent(KeyEvent.KEYCODE_2, depressed); | 224 JniInterface.sendKeyEvent(KeyEvent.KEYCODE_2, pressed); |
| 201 break; | 225 break; |
| 202 | 226 |
| 203 case KeyEvent.KEYCODE_POUND: | 227 case KeyEvent.KEYCODE_POUND: |
| 204 JniInterface.sendKeyEvent(KeyEvent.KEYCODE_SHIFT_LEFT, depressed
); | 228 JniInterface.sendKeyEvent(KeyEvent.KEYCODE_SHIFT_LEFT, pressed); |
| 205 JniInterface.sendKeyEvent(KeyEvent.KEYCODE_3, depressed); | 229 JniInterface.sendKeyEvent(KeyEvent.KEYCODE_3, pressed); |
| 206 break; | 230 break; |
| 207 | 231 |
| 208 case KeyEvent.KEYCODE_STAR: | 232 case KeyEvent.KEYCODE_STAR: |
| 209 JniInterface.sendKeyEvent(KeyEvent.KEYCODE_SHIFT_LEFT, depressed
); | 233 JniInterface.sendKeyEvent(KeyEvent.KEYCODE_SHIFT_LEFT, pressed); |
| 210 JniInterface.sendKeyEvent(KeyEvent.KEYCODE_8, depressed); | 234 JniInterface.sendKeyEvent(KeyEvent.KEYCODE_8, pressed); |
| 211 break; | 235 break; |
| 212 | 236 |
| 213 case KeyEvent.KEYCODE_PLUS: | 237 case KeyEvent.KEYCODE_PLUS: |
| 214 JniInterface.sendKeyEvent(KeyEvent.KEYCODE_SHIFT_LEFT, depressed
); | 238 JniInterface.sendKeyEvent(KeyEvent.KEYCODE_SHIFT_LEFT, pressed); |
| 215 JniInterface.sendKeyEvent(KeyEvent.KEYCODE_EQUALS, depressed); | 239 JniInterface.sendKeyEvent(KeyEvent.KEYCODE_EQUALS, pressed); |
| 216 break; | 240 break; |
| 217 | 241 |
| 218 default: | 242 default: |
| 219 // We try to send all other key codes to the host directly. | 243 // We try to send all other key codes to the host directly. |
| 220 JniInterface.sendKeyEvent(event.getKeyCode(), depressed); | 244 JniInterface.sendKeyEvent(keyCode, pressed); |
| 221 } | 245 } |
| 222 | 246 |
| 223 return super.dispatchKeyEvent(event); | 247 return super.dispatchKeyEvent(event); |
| 224 } | 248 } |
| 225 } | 249 } |
| OLD | NEW |