Desktop.java revision effb81e5f8246d0db0270817048dc992db66e9fb
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
3// found in the LICENSE file.
4
5package org.chromium.chromoting;
6
7import android.app.Activity;
8import android.content.res.Configuration;
9import android.os.Bundle;
10import android.view.KeyEvent;
11import android.view.Menu;
12import android.view.MenuItem;
13import android.view.View;
14import android.view.inputmethod.InputMethodManager;
15import android.widget.ImageButton;
16
17import org.chromium.chromoting.jni.JniInterface;
18
19/**
20 * A simple screen that does nothing except display a DesktopView and notify it of rotations.
21 */
22public class Desktop extends Activity {
23    /** Web page to be displayed in the Help screen when launched from this activity. */
24    private static final String HELP_URL =
25            "http://support.google.com/chrome/?p=mobile_crd_connecthost";
26
27    /** The surface that displays the remote host's desktop feed. */
28    private DesktopView mRemoteHostDesktop;
29
30    /** The button used to show the action bar. */
31    private ImageButton mOverlayButton;
32
33    /** Called when the activity is first created. */
34    @Override
35    public void onCreate(Bundle savedInstanceState) {
36        super.onCreate(savedInstanceState);
37        setContentView(R.layout.desktop);
38        mRemoteHostDesktop = (DesktopView)findViewById(R.id.desktop_view);
39        mOverlayButton = (ImageButton)findViewById(R.id.desktop_overlay_button);
40        mRemoteHostDesktop.setDesktop(this);
41
42        // Ensure the button is initially hidden.
43        showActionBar();
44    }
45
46    /** Called when the activity is finally finished. */
47    @Override
48    public void onDestroy() {
49        super.onDestroy();
50        JniInterface.disconnectFromHost();
51    }
52
53    /** Called when the display is rotated (as registered in the manifest). */
54    @Override
55    public void onConfigurationChanged(Configuration newConfig) {
56        super.onConfigurationChanged(newConfig);
57        mRemoteHostDesktop.onScreenConfigurationChanged();
58    }
59
60    /** Called to initialize the action bar. */
61    @Override
62    public boolean onCreateOptionsMenu(Menu menu) {
63        getMenuInflater().inflate(R.menu.desktop_actionbar, menu);
64        return super.onCreateOptionsMenu(menu);
65    }
66
67    public void showActionBar() {
68        mOverlayButton.setVisibility(View.INVISIBLE);
69        getActionBar().show();
70    }
71
72    public void hideActionBar() {
73        mOverlayButton.setVisibility(View.VISIBLE);
74        getActionBar().hide();
75    }
76
77    /** The overlay button's onClick handler. */
78    public void onOverlayButtonPressed(View view) {
79        showActionBar();
80    }
81
82    /** Called whenever an action bar button is pressed. */
83    @Override
84    public boolean onOptionsItemSelected(MenuItem item) {
85        switch (item.getItemId()) {
86            case R.id.actionbar_keyboard:
87                ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)).toggleSoftInput(0, 0);
88                return true;
89
90            case R.id.actionbar_hide:
91                hideActionBar();
92                return true;
93
94            case R.id.actionbar_disconnect:
95                JniInterface.disconnectFromHost();
96                return true;
97
98            case R.id.actionbar_send_ctrl_alt_del:
99                {
100                    int[] keys = {
101                        KeyEvent.KEYCODE_CTRL_LEFT,
102                        KeyEvent.KEYCODE_ALT_LEFT,
103                        KeyEvent.KEYCODE_FORWARD_DEL,
104                    };
105                    for (int key : keys) {
106                        JniInterface.sendKeyEvent(key, true);
107                    }
108                    for (int key : keys) {
109                        JniInterface.sendKeyEvent(key, false);
110                    }
111                }
112                return true;
113
114            case R.id.actionbar_help:
115                HelpActivity.launch(this, HELP_URL);
116                return true;
117
118            default:
119                return super.onOptionsItemSelected(item);
120        }
121    }
122
123    /**
124     * Called once when a keyboard key is pressed, then again when that same key is released. This
125     * is not guaranteed to be notified of all soft keyboard events: certian keyboards might not
126     * call it at all, while others might skip it in certain situations (e.g. swipe input).
127     */
128    @Override
129    public boolean dispatchKeyEvent(KeyEvent event) {
130        // Send ACTION_MULTIPLE event as TextEvent.
131        //
132        // TODO(sergeyu): For all keys on English keyboard Android generates
133        // ACTION_DOWN/ACTION_UP events, so they are sent as KeyEvent instead of
134        // TextEvent. As result the host may handle them as non-English chars
135        // when it has non-English layout selected, which might be confusing for
136        // the user. This code should be fixed to send all text input events as
137        // TextEvent, but it cannot be done now because not all hosts support
138        // TextEvent. Also, to handle keyboard shortcuts properly this code will
139        // need to track the state of modifier keys (such as Ctrl or Alt) and
140        // send KeyEvents in the case any of the modifier keys are pressed.
141        if (event.getAction() == KeyEvent.ACTION_MULTIPLE) {
142            JniInterface.sendTextEvent(event.getCharacters());
143            return super.dispatchKeyEvent(event);
144        }
145
146        boolean depressed = event.getAction() == KeyEvent.ACTION_DOWN;
147
148        switch (event.getKeyCode()) {
149            case KeyEvent.KEYCODE_AT:
150                JniInterface.sendKeyEvent(KeyEvent.KEYCODE_SHIFT_LEFT, depressed);
151                JniInterface.sendKeyEvent(KeyEvent.KEYCODE_2, depressed);
152                break;
153
154            case KeyEvent.KEYCODE_POUND:
155                JniInterface.sendKeyEvent(KeyEvent.KEYCODE_SHIFT_LEFT, depressed);
156                JniInterface.sendKeyEvent(KeyEvent.KEYCODE_3, depressed);
157                break;
158
159            case KeyEvent.KEYCODE_STAR:
160                JniInterface.sendKeyEvent(KeyEvent.KEYCODE_SHIFT_LEFT, depressed);
161                JniInterface.sendKeyEvent(KeyEvent.KEYCODE_8, depressed);
162                break;
163
164            case KeyEvent.KEYCODE_PLUS:
165                JniInterface.sendKeyEvent(KeyEvent.KEYCODE_SHIFT_LEFT, depressed);
166                JniInterface.sendKeyEvent(KeyEvent.KEYCODE_EQUALS, depressed);
167                break;
168
169            default:
170                // We try to send all other key codes to the host directly.
171                JniInterface.sendKeyEvent(event.getKeyCode(), depressed);
172        }
173
174        return super.dispatchKeyEvent(event);
175    }
176}
177