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.AlertDialog; | 8 import android.app.AlertDialog; |
9 import android.content.DialogInterface; | 9 import android.content.DialogInterface; |
10 import android.content.Intent; | 10 import android.content.Intent; |
(...skipping 13 matching lines...) Expand all Loading... |
24 import android.view.MenuItem; | 24 import android.view.MenuItem; |
25 import android.view.MotionEvent; | 25 import android.view.MotionEvent; |
26 import android.view.View; | 26 import android.view.View; |
27 import android.view.View.OnLayoutChangeListener; | 27 import android.view.View.OnLayoutChangeListener; |
28 import android.view.View.OnTouchListener; | 28 import android.view.View.OnTouchListener; |
29 import android.view.inputmethod.InputMethodManager; | 29 import android.view.inputmethod.InputMethodManager; |
30 | 30 |
31 import org.chromium.chromoting.cardboard.DesktopActivity; | 31 import org.chromium.chromoting.cardboard.DesktopActivity; |
32 import org.chromium.chromoting.help.HelpContext; | 32 import org.chromium.chromoting.help.HelpContext; |
33 import org.chromium.chromoting.help.HelpSingleton; | 33 import org.chromium.chromoting.help.HelpSingleton; |
34 import org.chromium.chromoting.jni.Client; | 34 import org.chromium.chromoting.jni.JniInterface; |
35 | 35 |
36 import java.util.List; | 36 import java.util.List; |
37 import java.util.Set; | 37 import java.util.Set; |
38 import java.util.TreeSet; | 38 import java.util.TreeSet; |
39 | 39 |
40 /** | 40 /** |
41 * A simple screen that does nothing except display a DesktopView and notify it
of rotations. | 41 * A simple screen that does nothing except display a DesktopView and notify it
of rotations. |
42 */ | 42 */ |
43 public class Desktop | 43 public class Desktop |
44 extends AppCompatActivity implements View.OnSystemUiVisibilityChangeList
ener, | 44 extends AppCompatActivity implements View.OnSystemUiVisibilityChangeList
ener, |
(...skipping 17 matching lines...) Expand all Loading... |
62 | 62 |
63 /** Preference used to track the last input mode selected by the user. */ | 63 /** Preference used to track the last input mode selected by the user. */ |
64 private static final String PREFERENCE_INPUT_MODE = "input_mode"; | 64 private static final String PREFERENCE_INPUT_MODE = "input_mode"; |
65 | 65 |
66 /** The amount of time to wait to hide the Actionbar after user input is see
n. */ | 66 /** The amount of time to wait to hide the Actionbar after user input is see
n. */ |
67 private static final int ACTIONBAR_AUTO_HIDE_DELAY_MS = 3000; | 67 private static final int ACTIONBAR_AUTO_HIDE_DELAY_MS = 3000; |
68 | 68 |
69 /** The surface that displays the remote host's desktop feed. */ | 69 /** The surface that displays the remote host's desktop feed. */ |
70 private DesktopView mRemoteHostDesktop; | 70 private DesktopView mRemoteHostDesktop; |
71 | 71 |
72 private Client mClient; | |
73 | |
74 /** Set of pressed keys for which we've sent TextEvent. */ | 72 /** Set of pressed keys for which we've sent TextEvent. */ |
75 private Set<Integer> mPressedTextKeys = new TreeSet<Integer>(); | 73 private Set<Integer> mPressedTextKeys = new TreeSet<Integer>(); |
76 | 74 |
77 private ActivityLifecycleListener mActivityLifecycleListener; | 75 private ActivityLifecycleListener mActivityLifecycleListener; |
78 | 76 |
79 /** Flag to indicate whether the current activity is switching to Cardboard
desktop activity. */ | 77 /** Flag to indicate whether the current activity is switching to Cardboard
desktop activity. */ |
80 private boolean mSwitchToCardboardDesktopActivity; | 78 private boolean mSwitchToCardboardDesktopActivity; |
81 | 79 |
82 /** Flag to indicate whether to manually hide the system UI when the OSK is
dismissed. */ | 80 /** Flag to indicate whether to manually hide the system UI when the OSK is
dismissed. */ |
83 private boolean mHideSystemUIOnSoftKeyboardDismiss = false; | 81 private boolean mHideSystemUIOnSoftKeyboardDismiss = false; |
(...skipping 13 matching lines...) Expand all Loading... |
97 /** Indicates whether the remote host supports touch injection. */ | 95 /** Indicates whether the remote host supports touch injection. */ |
98 private CapabilityManager.HostCapability mHostTouchCapability = | 96 private CapabilityManager.HostCapability mHostTouchCapability = |
99 CapabilityManager.HostCapability.UNKNOWN; | 97 CapabilityManager.HostCapability.UNKNOWN; |
100 | 98 |
101 /** Called when the activity is first created. */ | 99 /** Called when the activity is first created. */ |
102 @Override | 100 @Override |
103 public void onCreate(Bundle savedInstanceState) { | 101 public void onCreate(Bundle savedInstanceState) { |
104 super.onCreate(savedInstanceState); | 102 super.onCreate(savedInstanceState); |
105 setContentView(R.layout.desktop); | 103 setContentView(R.layout.desktop); |
106 | 104 |
107 mClient = Client.getInstance(); | |
108 | |
109 mToolbar = (Toolbar) findViewById(R.id.toolbar); | 105 mToolbar = (Toolbar) findViewById(R.id.toolbar); |
110 setSupportActionBar(mToolbar); | 106 setSupportActionBar(mToolbar); |
111 | 107 |
112 mRemoteHostDesktop = (DesktopView) findViewById(R.id.desktop_view); | 108 mRemoteHostDesktop = (DesktopView) findViewById(R.id.desktop_view); |
113 mRemoteHostDesktop.setDesktop(this); | 109 mRemoteHostDesktop.setDesktop(this); |
114 mRemoteHostDesktop.setClient(mClient); | |
115 mSwitchToCardboardDesktopActivity = false; | 110 mSwitchToCardboardDesktopActivity = false; |
116 | 111 |
117 getSupportActionBar().setDisplayShowTitleEnabled(false); | 112 getSupportActionBar().setDisplayShowTitleEnabled(false); |
118 getSupportActionBar().setDisplayHomeAsUpEnabled(true); | 113 getSupportActionBar().setDisplayHomeAsUpEnabled(true); |
119 | 114 |
120 // For this Activity, the home button in the action bar acts as a Discon
nect button, so | 115 // For this Activity, the home button in the action bar acts as a Discon
nect button, so |
121 // set the description for accessibility/screen readers. | 116 // set the description for accessibility/screen readers. |
122 getSupportActionBar().setHomeActionContentDescription(R.string.disconnec
t_myself_button); | 117 getSupportActionBar().setHomeActionContentDescription(R.string.disconnec
t_myself_button); |
123 | 118 |
124 // The action bar is already shown when the activity is started however
calling the | 119 // The action bar is already shown when the activity is started however
calling the |
125 // function below will set our preferred system UI flags which will adju
st the layout | 120 // function below will set our preferred system UI flags which will adju
st the layout |
126 // size of the canvas and we can avoid an initial resize event. | 121 // size of the canvas and we can avoid an initial resize event. |
127 showActionBar(); | 122 showActionBar(); |
128 | 123 |
129 View decorView = getWindow().getDecorView(); | 124 View decorView = getWindow().getDecorView(); |
130 decorView.setOnSystemUiVisibilityChangeListener(this); | 125 decorView.setOnSystemUiVisibilityChangeListener(this); |
131 | 126 |
132 mActivityLifecycleListener = mClient.getCapabilityManager().onActivityAc
ceptingListener( | 127 mActivityLifecycleListener = CapabilityManager.getInstance().onActivityA
cceptingListener( |
133 this, Capabilities.CAST_CAPABILITY); | 128 this, Capabilities.CAST_CAPABILITY); |
134 mActivityLifecycleListener.onActivityCreated(this, savedInstanceState); | 129 mActivityLifecycleListener.onActivityCreated(this, savedInstanceState); |
135 | 130 |
136 mInputMode = getInitialInputModeValue(); | 131 mInputMode = getInitialInputModeValue(); |
137 | 132 |
138 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { | 133 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { |
139 attachKeyboardVisibilityListener(); | 134 attachKeyboardVisibilityListener(); |
140 | 135 |
141 // Only create an Autohide task if the system supports immersive ful
lscreen mode. Older | 136 // Only create an Autohide task if the system supports immersive ful
lscreen mode. Older |
142 // versions of the OS benefit less from this functionality and we do
n't want to change | 137 // versions of the OS benefit less from this functionality and we do
n't want to change |
(...skipping 18 matching lines...) Expand all Loading... |
161 }); | 156 }); |
162 } else { | 157 } else { |
163 mRemoteHostDesktop.setFitsSystemWindows(true); | 158 mRemoteHostDesktop.setFitsSystemWindows(true); |
164 } | 159 } |
165 } | 160 } |
166 | 161 |
167 @Override | 162 @Override |
168 protected void onStart() { | 163 protected void onStart() { |
169 super.onStart(); | 164 super.onStart(); |
170 mActivityLifecycleListener.onActivityStarted(this); | 165 mActivityLifecycleListener.onActivityStarted(this); |
171 mClient.enableVideoChannel(true); | 166 JniInterface.enableVideoChannel(true); |
172 mRemoteHostDesktop.attachRedrawCallback(); | 167 mRemoteHostDesktop.attachRedrawCallback(); |
173 mClient.getCapabilityManager().addListener(this); | 168 CapabilityManager.getInstance().addListener(this); |
174 } | 169 } |
175 | 170 |
176 @Override | 171 @Override |
177 protected void onPause() { | 172 protected void onPause() { |
178 if (isFinishing()) mActivityLifecycleListener.onActivityPaused(this); | 173 if (isFinishing()) mActivityLifecycleListener.onActivityPaused(this); |
179 super.onPause(); | 174 super.onPause(); |
180 if (!mSwitchToCardboardDesktopActivity) { | 175 if (!mSwitchToCardboardDesktopActivity) { |
181 mClient.enableVideoChannel(false); | 176 JniInterface.enableVideoChannel(false); |
182 } | 177 } |
183 stopActionBarAutoHideTimer(); | 178 stopActionBarAutoHideTimer(); |
184 } | 179 } |
185 | 180 |
186 @Override | 181 @Override |
187 public void onResume() { | 182 public void onResume() { |
188 super.onResume(); | 183 super.onResume(); |
189 mActivityLifecycleListener.onActivityResumed(this); | 184 mActivityLifecycleListener.onActivityResumed(this); |
190 mClient.enableVideoChannel(true); | 185 JniInterface.enableVideoChannel(true); |
191 startActionBarAutoHideTimer(); | 186 startActionBarAutoHideTimer(); |
192 } | 187 } |
193 | 188 |
194 @Override | 189 @Override |
195 protected void onStop() { | 190 protected void onStop() { |
196 mClient.getCapabilityManager().removeListener(this); | 191 CapabilityManager.getInstance().removeListener(this); |
197 mActivityLifecycleListener.onActivityStopped(this); | 192 mActivityLifecycleListener.onActivityStopped(this); |
198 super.onStop(); | 193 super.onStop(); |
199 if (mSwitchToCardboardDesktopActivity) { | 194 if (mSwitchToCardboardDesktopActivity) { |
200 mSwitchToCardboardDesktopActivity = false; | 195 mSwitchToCardboardDesktopActivity = false; |
201 } else { | 196 } else { |
202 mClient.enableVideoChannel(false); | 197 JniInterface.enableVideoChannel(false); |
203 } | 198 } |
204 } | 199 } |
205 | 200 |
206 /** Called to initialize the action bar. */ | 201 /** Called to initialize the action bar. */ |
207 @Override | 202 @Override |
208 public boolean onCreateOptionsMenu(Menu menu) { | 203 public boolean onCreateOptionsMenu(Menu menu) { |
209 getMenuInflater().inflate(R.menu.desktop_actionbar, menu); | 204 getMenuInflater().inflate(R.menu.desktop_actionbar, menu); |
210 | 205 |
211 mActivityLifecycleListener.onActivityCreatedOptionsMenu(this, menu); | 206 mActivityLifecycleListener.onActivityCreatedOptionsMenu(this, menu); |
212 | 207 |
(...skipping 274 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
487 } | 482 } |
488 if (id == R.id.actionbar_keyboard) { | 483 if (id == R.id.actionbar_keyboard) { |
489 ((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE)).toggle
SoftInput(0, 0); | 484 ((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE)).toggle
SoftInput(0, 0); |
490 return true; | 485 return true; |
491 } | 486 } |
492 if (id == R.id.actionbar_hide) { | 487 if (id == R.id.actionbar_hide) { |
493 hideActionBar(); | 488 hideActionBar(); |
494 return true; | 489 return true; |
495 } | 490 } |
496 if (id == R.id.actionbar_disconnect || id == android.R.id.home) { | 491 if (id == R.id.actionbar_disconnect || id == android.R.id.home) { |
497 mClient.destroy(); | 492 JniInterface.disconnectFromHost(); |
498 return true; | 493 return true; |
499 } | 494 } |
500 if (id == R.id.actionbar_send_ctrl_alt_del) { | 495 if (id == R.id.actionbar_send_ctrl_alt_del) { |
501 int[] keys = { | 496 int[] keys = { |
502 KeyEvent.KEYCODE_CTRL_LEFT, | 497 KeyEvent.KEYCODE_CTRL_LEFT, |
503 KeyEvent.KEYCODE_ALT_LEFT, | 498 KeyEvent.KEYCODE_ALT_LEFT, |
504 KeyEvent.KEYCODE_FORWARD_DEL, | 499 KeyEvent.KEYCODE_FORWARD_DEL, |
505 }; | 500 }; |
506 for (int key : keys) { | 501 for (int key : keys) { |
507 mClient.sendKeyEvent(0, key, true); | 502 JniInterface.sendKeyEvent(0, key, true); |
508 } | 503 } |
509 for (int key : keys) { | 504 for (int key : keys) { |
510 mClient.sendKeyEvent(0, key, false); | 505 JniInterface.sendKeyEvent(0, key, false); |
511 } | 506 } |
512 return true; | 507 return true; |
513 } | 508 } |
514 if (id == R.id.actionbar_help) { | 509 if (id == R.id.actionbar_help) { |
515 HelpSingleton.getInstance().launchHelp(this, HelpContext.DESKTOP); | 510 HelpSingleton.getInstance().launchHelp(this, HelpContext.DESKTOP); |
516 return true; | 511 return true; |
517 } | 512 } |
518 return super.onOptionsItemSelected(item); | 513 return super.onOptionsItemSelected(item); |
519 } | 514 } |
520 | 515 |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
607 * Called once when a keyboard key is pressed, then again when that same key
is released. This | 602 * Called once when a keyboard key is pressed, then again when that same key
is released. This |
608 * is not guaranteed to be notified of all soft keyboard events: certian key
boards might not | 603 * is not guaranteed to be notified of all soft keyboard events: certian key
boards might not |
609 * call it at all, while others might skip it in certain situations (e.g. sw
ipe input). | 604 * call it at all, while others might skip it in certain situations (e.g. sw
ipe input). |
610 */ | 605 */ |
611 @Override | 606 @Override |
612 public boolean dispatchKeyEvent(KeyEvent event) { | 607 public boolean dispatchKeyEvent(KeyEvent event) { |
613 int keyCode = event.getKeyCode(); | 608 int keyCode = event.getKeyCode(); |
614 | 609 |
615 // Dispatch the back button to the system to handle navigation | 610 // Dispatch the back button to the system to handle navigation |
616 if (keyCode == KeyEvent.KEYCODE_BACK) { | 611 if (keyCode == KeyEvent.KEYCODE_BACK) { |
617 mClient.destroy(); | 612 JniInterface.disconnectFromHost(); |
618 return super.dispatchKeyEvent(event); | 613 return super.dispatchKeyEvent(event); |
619 } | 614 } |
620 | 615 |
621 boolean pressed = event.getAction() == KeyEvent.ACTION_DOWN; | 616 boolean pressed = event.getAction() == KeyEvent.ACTION_DOWN; |
622 | 617 |
623 // Physical keyboard must work as if it is connected to the remote host | 618 // Physical keyboard must work as if it is connected to the remote host |
624 // and so events coming from physical keyboard never generate text | 619 // and so events coming from physical keyboard never generate text |
625 // events. Also scan codes must be used instead of key code, so that | 620 // events. Also scan codes must be used instead of key code, so that |
626 // the keyboard layout selected on the client doesn't affect the key | 621 // the keyboard layout selected on the client doesn't affect the key |
627 // codes sent to the host. | 622 // codes sent to the host. |
628 if (event.getDeviceId() != KeyCharacterMap.VIRTUAL_KEYBOARD) { | 623 if (event.getDeviceId() != KeyCharacterMap.VIRTUAL_KEYBOARD) { |
629 return mClient.sendKeyEvent(event.getScanCode(), 0, pressed); | 624 return JniInterface.sendKeyEvent(event.getScanCode(), 0, pressed); |
630 } | 625 } |
631 | 626 |
632 // Events received from software keyboards generate TextEvent in two | 627 // Events received from software keyboards generate TextEvent in two |
633 // cases: | 628 // cases: |
634 // 1. This is an ACTION_MULTIPLE event. | 629 // 1. This is an ACTION_MULTIPLE event. |
635 // 2. Ctrl, Alt and Meta are not pressed. | 630 // 2. Ctrl, Alt and Meta are not pressed. |
636 // This ensures that on-screen keyboard always injects input that | 631 // This ensures that on-screen keyboard always injects input that |
637 // correspond to what user sees on the screen, while physical keyboard | 632 // correspond to what user sees on the screen, while physical keyboard |
638 // acts as if it is connected to the remote host. | 633 // acts as if it is connected to the remote host. |
639 if (event.getAction() == KeyEvent.ACTION_MULTIPLE) { | 634 if (event.getAction() == KeyEvent.ACTION_MULTIPLE) { |
640 mClient.sendTextEvent(event.getCharacters()); | 635 JniInterface.sendTextEvent(event.getCharacters()); |
641 return true; | 636 return true; |
642 } | 637 } |
643 | 638 |
644 // For Enter getUnicodeChar() returns 10 (line feed), but we still | 639 // For Enter getUnicodeChar() returns 10 (line feed), but we still |
645 // want to send it as KeyEvent. | 640 // want to send it as KeyEvent. |
646 int unicode = keyCode != KeyEvent.KEYCODE_ENTER ? event.getUnicodeChar()
: 0; | 641 int unicode = keyCode != KeyEvent.KEYCODE_ENTER ? event.getUnicodeChar()
: 0; |
647 | 642 |
648 boolean no_modifiers = !event.isAltPressed() | 643 boolean no_modifiers = !event.isAltPressed() |
649 && !event.isCtrlPressed() && !event.isMetaPressed(); | 644 && !event.isCtrlPressed() && !event.isMetaPressed(); |
650 | 645 |
651 if (pressed && unicode != 0 && no_modifiers) { | 646 if (pressed && unicode != 0 && no_modifiers) { |
652 mPressedTextKeys.add(keyCode); | 647 mPressedTextKeys.add(keyCode); |
653 int[] codePoints = { unicode }; | 648 int[] codePoints = { unicode }; |
654 mClient.sendTextEvent(new String(codePoints, 0, 1)); | 649 JniInterface.sendTextEvent(new String(codePoints, 0, 1)); |
655 return true; | 650 return true; |
656 } | 651 } |
657 | 652 |
658 if (!pressed && mPressedTextKeys.contains(keyCode)) { | 653 if (!pressed && mPressedTextKeys.contains(keyCode)) { |
659 mPressedTextKeys.remove(keyCode); | 654 mPressedTextKeys.remove(keyCode); |
660 return true; | 655 return true; |
661 } | 656 } |
662 | 657 |
663 switch (keyCode) { | 658 switch (keyCode) { |
664 // KEYCODE_AT, KEYCODE_POUND, KEYCODE_STAR and KEYCODE_PLUS are | 659 // KEYCODE_AT, KEYCODE_POUND, KEYCODE_STAR and KEYCODE_PLUS are |
665 // deprecated, but they still need to be here for older devices and | 660 // deprecated, but they still need to be here for older devices and |
666 // third-party keyboards that may still generate these events. See | 661 // third-party keyboards that may still generate these events. See |
667 // https://source.android.com/devices/input/keyboard-devices.html#le
gacy-unsupported-keys | 662 // https://source.android.com/devices/input/keyboard-devices.html#le
gacy-unsupported-keys |
668 case KeyEvent.KEYCODE_AT: | 663 case KeyEvent.KEYCODE_AT: |
669 mClient.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed); | 664 JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, presse
d); |
670 mClient.sendKeyEvent(0, KeyEvent.KEYCODE_2, pressed); | 665 JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_2, pressed); |
671 return true; | 666 return true; |
672 | 667 |
673 case KeyEvent.KEYCODE_POUND: | 668 case KeyEvent.KEYCODE_POUND: |
674 mClient.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed); | 669 JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, presse
d); |
675 mClient.sendKeyEvent(0, KeyEvent.KEYCODE_3, pressed); | 670 JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_3, pressed); |
676 return true; | 671 return true; |
677 | 672 |
678 case KeyEvent.KEYCODE_STAR: | 673 case KeyEvent.KEYCODE_STAR: |
679 mClient.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed); | 674 JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, presse
d); |
680 mClient.sendKeyEvent(0, KeyEvent.KEYCODE_8, pressed); | 675 JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_8, pressed); |
681 return true; | 676 return true; |
682 | 677 |
683 case KeyEvent.KEYCODE_PLUS: | 678 case KeyEvent.KEYCODE_PLUS: |
684 mClient.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed); | 679 JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, presse
d); |
685 mClient.sendKeyEvent(0, KeyEvent.KEYCODE_EQUALS, pressed); | 680 JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_EQUALS, pressed); |
686 return true; | 681 return true; |
687 | 682 |
688 default: | 683 default: |
689 // We try to send all other key codes to the host directly. | 684 // We try to send all other key codes to the host directly. |
690 return mClient.sendKeyEvent(0, keyCode, pressed); | 685 return JniInterface.sendKeyEvent(0, keyCode, pressed); |
691 } | 686 } |
692 } | 687 } |
693 } | 688 } |
OLD | NEW |