Chromium Code Reviews| 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.content.DialogInterface; | 8 import android.content.DialogInterface; |
| 9 import android.content.Intent; | 9 import android.content.Intent; |
| 10 import android.content.pm.ApplicationInfo; | 10 import android.content.pm.ApplicationInfo; |
| 11 import android.content.pm.PackageManager; | 11 import android.content.pm.PackageManager; |
| 12 import android.content.pm.PackageManager.NameNotFoundException; | 12 import android.content.pm.PackageManager.NameNotFoundException; |
| 13 import android.os.Build; | 13 import android.os.Build; |
| 14 import android.os.Bundle; | 14 import android.os.Bundle; |
| 15 import android.os.Handler; | 15 import android.os.Handler; |
| 16 import android.support.v7.app.ActionBar.OnMenuVisibilityListener; | 16 import android.support.v7.app.ActionBar.OnMenuVisibilityListener; |
| 17 import android.support.v7.app.AlertDialog; | 17 import android.support.v7.app.AlertDialog; |
| 18 import android.support.v7.app.AppCompatActivity; | 18 import android.support.v7.app.AppCompatActivity; |
| 19 import android.support.v7.widget.Toolbar; | 19 import android.support.v7.widget.Toolbar; |
| 20 import android.view.KeyCharacterMap; | |
| 21 import android.view.KeyEvent; | 20 import android.view.KeyEvent; |
| 22 import android.view.Menu; | 21 import android.view.Menu; |
| 23 import android.view.MenuItem; | 22 import android.view.MenuItem; |
| 24 import android.view.MotionEvent; | 23 import android.view.MotionEvent; |
| 25 import android.view.View; | 24 import android.view.View; |
| 26 import android.view.View.OnLayoutChangeListener; | 25 import android.view.View.OnLayoutChangeListener; |
| 27 import android.view.View.OnTouchListener; | 26 import android.view.View.OnTouchListener; |
| 28 import android.view.inputmethod.InputMethodManager; | 27 import android.view.inputmethod.InputMethodManager; |
| 29 | 28 |
| 30 import org.chromium.chromoting.cardboard.DesktopActivity; | 29 import org.chromium.chromoting.cardboard.DesktopActivity; |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 65 /** The amount of time to wait to hide the Actionbar after user input is see n. */ | 64 /** The amount of time to wait to hide the Actionbar after user input is see n. */ |
| 66 private static final int ACTIONBAR_AUTO_HIDE_DELAY_MS = 3000; | 65 private static final int ACTIONBAR_AUTO_HIDE_DELAY_MS = 3000; |
| 67 | 66 |
| 68 private final Event.Raisable<SoftInputMethodVisibilityChangedEventParameter> | 67 private final Event.Raisable<SoftInputMethodVisibilityChangedEventParameter> |
| 69 mOnSoftInputMethodVisibilityChanged = new Event.Raisable<>(); | 68 mOnSoftInputMethodVisibilityChanged = new Event.Raisable<>(); |
| 70 | 69 |
| 71 private final Event.Raisable<InputModeChangedEventParameter> mOnInputModeCha nged = | 70 private final Event.Raisable<InputModeChangedEventParameter> mOnInputModeCha nged = |
| 72 new Event.Raisable<>(); | 71 new Event.Raisable<>(); |
| 73 | 72 |
| 74 private Client mClient; | 73 private Client mClient; |
| 74 private InputInjectorWrapper mInjector; | |
| 75 | 75 |
| 76 /** Set of pressed keys for which we've sent TextEvent. */ | 76 /** Set of pressed keys for which we've sent TextEvent. */ |
| 77 private Set<Integer> mPressedTextKeys = new TreeSet<Integer>(); | 77 private Set<Integer> mPressedTextKeys = new TreeSet<Integer>(); |
|
Lambros
2016/06/17 23:00:09
Remove this, it's not used.
Hzj_jie
2016/06/19 23:41:38
Done.
| |
| 78 | 78 |
| 79 private ActivityLifecycleListener mActivityLifecycleListener; | 79 private ActivityLifecycleListener mActivityLifecycleListener; |
| 80 | 80 |
| 81 /** Flag to indicate whether the current activity is switching to Cardboard desktop activity. */ | 81 /** Flag to indicate whether the current activity is switching to Cardboard desktop activity. */ |
| 82 private boolean mSwitchToCardboardDesktopActivity; | 82 private boolean mSwitchToCardboardDesktopActivity; |
| 83 | 83 |
| 84 /** Flag to indicate whether to manually hide the system UI when the OSK is dismissed. */ | 84 /** Flag to indicate whether to manually hide the system UI when the OSK is dismissed. */ |
| 85 private boolean mHideSystemUIOnSoftKeyboardDismiss = false; | 85 private boolean mHideSystemUIOnSoftKeyboardDismiss = false; |
| 86 | 86 |
| 87 /** Indicates whether a Soft Input UI (such as a keyboard) is visible. */ | 87 /** Indicates whether a Soft Input UI (such as a keyboard) is visible. */ |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 100 private CapabilityManager.HostCapability mHostTouchCapability = | 100 private CapabilityManager.HostCapability mHostTouchCapability = |
| 101 CapabilityManager.HostCapability.UNKNOWN; | 101 CapabilityManager.HostCapability.UNKNOWN; |
| 102 | 102 |
| 103 /** Called when the activity is first created. */ | 103 /** Called when the activity is first created. */ |
| 104 @Override | 104 @Override |
| 105 public void onCreate(Bundle savedInstanceState) { | 105 public void onCreate(Bundle savedInstanceState) { |
| 106 super.onCreate(savedInstanceState); | 106 super.onCreate(savedInstanceState); |
| 107 setContentView(R.layout.desktop); | 107 setContentView(R.layout.desktop); |
| 108 | 108 |
| 109 mClient = Client.getInstance(); | 109 mClient = Client.getInstance(); |
| 110 mInjector = new InputInjectorWrapper(mClient); | |
| 110 | 111 |
| 111 mToolbar = (Toolbar) findViewById(R.id.toolbar); | 112 mToolbar = (Toolbar) findViewById(R.id.toolbar); |
| 112 setSupportActionBar(mToolbar); | 113 setSupportActionBar(mToolbar); |
| 113 | 114 |
| 114 DesktopView remoteHostDesktop = (DesktopView) findViewById(R.id.desktop_ view); | 115 DesktopView remoteHostDesktop = (DesktopView) findViewById(R.id.desktop_ view); |
| 115 remoteHostDesktop.init(this, mClient); | 116 remoteHostDesktop.init(this, mClient); |
| 116 mSwitchToCardboardDesktopActivity = false; | 117 mSwitchToCardboardDesktopActivity = false; |
| 117 | 118 |
| 118 getSupportActionBar().setDisplayShowTitleEnabled(false); | 119 getSupportActionBar().setDisplayShowTitleEnabled(false); |
| 119 getSupportActionBar().setDisplayHomeAsUpEnabled(true); | 120 getSupportActionBar().setDisplayHomeAsUpEnabled(true); |
| (...skipping 384 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 504 } | 505 } |
| 505 if (id == R.id.actionbar_hide) { | 506 if (id == R.id.actionbar_hide) { |
| 506 hideActionBar(); | 507 hideActionBar(); |
| 507 return true; | 508 return true; |
| 508 } | 509 } |
| 509 if (id == R.id.actionbar_disconnect || id == android.R.id.home) { | 510 if (id == R.id.actionbar_disconnect || id == android.R.id.home) { |
| 510 mClient.destroy(); | 511 mClient.destroy(); |
| 511 return true; | 512 return true; |
| 512 } | 513 } |
| 513 if (id == R.id.actionbar_send_ctrl_alt_del) { | 514 if (id == R.id.actionbar_send_ctrl_alt_del) { |
| 514 int[] keys = { | 515 mInjector.sendCtrlAltDel(); |
| 515 KeyEvent.KEYCODE_CTRL_LEFT, | |
| 516 KeyEvent.KEYCODE_ALT_LEFT, | |
| 517 KeyEvent.KEYCODE_FORWARD_DEL, | |
| 518 }; | |
| 519 for (int key : keys) { | |
| 520 mClient.sendKeyEvent(0, key, true); | |
| 521 } | |
| 522 for (int key : keys) { | |
| 523 mClient.sendKeyEvent(0, key, false); | |
| 524 } | |
| 525 return true; | 516 return true; |
| 526 } | 517 } |
| 527 if (id == R.id.actionbar_help) { | 518 if (id == R.id.actionbar_help) { |
| 528 HelpSingleton.getInstance().launchHelp(this, HelpContext.DESKTOP); | 519 HelpSingleton.getInstance().launchHelp(this, HelpContext.DESKTOP); |
| 529 return true; | 520 return true; |
| 530 } | 521 } |
| 531 return super.onOptionsItemSelected(item); | 522 return super.onOptionsItemSelected(item); |
| 532 } | 523 } |
| 533 | 524 |
| 534 private void attachKeyboardVisibilityListener() { | 525 private void attachKeyboardVisibilityListener() { |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 617 startActivityForResult(intent, Chromoting.CARDBOARD_DESKTOP_ACTIVITY); | 608 startActivityForResult(intent, Chromoting.CARDBOARD_DESKTOP_ACTIVITY); |
| 618 } | 609 } |
| 619 | 610 |
| 620 /** | 611 /** |
| 621 * Called once when a keyboard key is pressed, then again when that same key is released. This | 612 * Called once when a keyboard key is pressed, then again when that same key is released. This |
| 622 * is not guaranteed to be notified of all soft keyboard events: certian key boards might not | 613 * is not guaranteed to be notified of all soft keyboard events: certian key boards might not |
| 623 * call it at all, while others might skip it in certain situations (e.g. sw ipe input). | 614 * call it at all, while others might skip it in certain situations (e.g. sw ipe input). |
| 624 */ | 615 */ |
| 625 @Override | 616 @Override |
| 626 public boolean dispatchKeyEvent(KeyEvent event) { | 617 public boolean dispatchKeyEvent(KeyEvent event) { |
| 627 int keyCode = event.getKeyCode(); | 618 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { |
| 628 | |
| 629 // Dispatch the back button to the system to handle navigation | |
| 630 if (keyCode == KeyEvent.KEYCODE_BACK) { | |
| 631 mClient.destroy(); | 619 mClient.destroy(); |
| 632 return super.dispatchKeyEvent(event); | 620 return super.dispatchKeyEvent(event); |
| 633 } | 621 } |
| 634 | 622 |
| 635 boolean pressed = event.getAction() == KeyEvent.ACTION_DOWN; | 623 return mInjector.sendKeyEvent(event); |
| 636 | |
| 637 // Physical keyboard must work as if it is connected to the remote host | |
| 638 // and so events coming from physical keyboard never generate text | |
| 639 // events. Also scan codes must be used instead of key code, so that | |
| 640 // the keyboard layout selected on the client doesn't affect the key | |
| 641 // codes sent to the host. | |
| 642 if (event.getDeviceId() != KeyCharacterMap.VIRTUAL_KEYBOARD) { | |
| 643 return mClient.sendKeyEvent(event.getScanCode(), 0, pressed); | |
| 644 } | |
| 645 | |
| 646 // Events received from software keyboards generate TextEvent in two | |
| 647 // cases: | |
| 648 // 1. This is an ACTION_MULTIPLE event. | |
| 649 // 2. Ctrl, Alt and Meta are not pressed. | |
| 650 // This ensures that on-screen keyboard always injects input that | |
| 651 // correspond to what user sees on the screen, while physical keyboard | |
| 652 // acts as if it is connected to the remote host. | |
| 653 if (event.getAction() == KeyEvent.ACTION_MULTIPLE) { | |
| 654 mClient.sendTextEvent(event.getCharacters()); | |
| 655 return true; | |
| 656 } | |
| 657 | |
| 658 // For Enter getUnicodeChar() returns 10 (line feed), but we still | |
| 659 // want to send it as KeyEvent. | |
| 660 int unicode = keyCode != KeyEvent.KEYCODE_ENTER ? event.getUnicodeChar() : 0; | |
| 661 | |
| 662 boolean no_modifiers = !event.isAltPressed() | |
| 663 && !event.isCtrlPressed() && !event.isMetaPressed(); | |
| 664 | |
| 665 if (pressed && unicode != 0 && no_modifiers) { | |
| 666 mPressedTextKeys.add(keyCode); | |
| 667 int[] codePoints = { unicode }; | |
| 668 mClient.sendTextEvent(new String(codePoints, 0, 1)); | |
| 669 return true; | |
| 670 } | |
| 671 | |
| 672 if (!pressed && mPressedTextKeys.contains(keyCode)) { | |
| 673 mPressedTextKeys.remove(keyCode); | |
| 674 return true; | |
| 675 } | |
| 676 | |
| 677 switch (keyCode) { | |
| 678 // KEYCODE_AT, KEYCODE_POUND, KEYCODE_STAR and KEYCODE_PLUS are | |
| 679 // deprecated, but they still need to be here for older devices and | |
| 680 // third-party keyboards that may still generate these events. See | |
| 681 // https://source.android.com/devices/input/keyboard-devices.html#le gacy-unsupported-keys | |
| 682 case KeyEvent.KEYCODE_AT: | |
| 683 mClient.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed); | |
| 684 mClient.sendKeyEvent(0, KeyEvent.KEYCODE_2, pressed); | |
| 685 return true; | |
| 686 | |
| 687 case KeyEvent.KEYCODE_POUND: | |
| 688 mClient.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed); | |
| 689 mClient.sendKeyEvent(0, KeyEvent.KEYCODE_3, pressed); | |
| 690 return true; | |
| 691 | |
| 692 case KeyEvent.KEYCODE_STAR: | |
| 693 mClient.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed); | |
| 694 mClient.sendKeyEvent(0, KeyEvent.KEYCODE_8, pressed); | |
| 695 return true; | |
| 696 | |
| 697 case KeyEvent.KEYCODE_PLUS: | |
| 698 mClient.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed); | |
| 699 mClient.sendKeyEvent(0, KeyEvent.KEYCODE_EQUALS, pressed); | |
| 700 return true; | |
| 701 | |
| 702 default: | |
| 703 // We try to send all other key codes to the host directly. | |
| 704 return mClient.sendKeyEvent(0, keyCode, pressed); | |
| 705 } | |
| 706 } | 624 } |
| 707 } | 625 } |
| OLD | NEW |