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