comparison src/com/five_ten_sg/connectbot/ConsoleActivity.java @ 0:0ce5cc452d02

initial version
author Carl Byington <carl@five-ten-sg.com>
date Thu, 22 May 2014 10:41:19 -0700
parents
children e0f5e706a655
comparison
equal deleted inserted replaced
-1:000000000000 0:0ce5cc452d02
1 /*
2 * ConnectBot: simple, powerful, open-source SSH client for Android
3 * Copyright 2007 Kenny Root, Jeffrey Sharkey
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package com.five_ten_sg.connectbot;
19
20 import java.io.File;
21 import java.net.URI;
22 import java.util.ArrayList;
23 import java.util.List;
24
25 import com.five_ten_sg.connectbot.bean.SelectionArea;
26 import com.five_ten_sg.connectbot.service.PromptHelper;
27 import com.five_ten_sg.connectbot.service.TerminalBridge;
28 import com.five_ten_sg.connectbot.service.TerminalKeyListener;
29 import com.five_ten_sg.connectbot.service.TerminalManager;
30 import com.five_ten_sg.connectbot.util.FileChooser;
31 import com.five_ten_sg.connectbot.util.FileChooserCallback;
32 import com.five_ten_sg.connectbot.util.PreferenceConstants;
33 import com.five_ten_sg.connectbot.util.TransferThread;
34 import android.app.Activity;
35 import android.app.AlertDialog;
36 import android.content.ComponentName;
37 import android.content.Context;
38 import android.content.DialogInterface;
39 import android.content.Intent;
40 import android.content.ServiceConnection;
41 import android.content.SharedPreferences;
42 import android.content.pm.ActivityInfo;
43 import android.content.res.Configuration;
44 import android.media.AudioManager;
45 import android.net.Uri;
46 import android.os.Bundle;
47 import android.os.Handler;
48 import android.os.IBinder;
49 import android.os.Message;
50 import android.preference.PreferenceManager;
51 import android.text.ClipboardManager;
52 import android.text.InputType;
53 import android.text.method.PasswordTransformationMethod;
54 import android.text.method.SingleLineTransformationMethod;
55 import android.util.FloatMath;
56 import android.util.Log;
57 import android.view.GestureDetector;
58 import android.view.KeyEvent;
59 import android.view.LayoutInflater;
60 import android.view.Menu;
61 import android.view.MenuItem;
62 import android.view.MenuItem.OnMenuItemClickListener;
63 import android.view.MotionEvent;
64 import android.view.View;
65 import android.view.View.OnClickListener;
66 import android.view.View.OnKeyListener;
67 import android.view.View.OnLongClickListener;
68 import android.view.View.OnTouchListener;
69 import android.view.ViewConfiguration;
70 import android.view.WindowManager;
71 import android.view.animation.Animation;
72 import android.view.animation.AnimationUtils;
73 import android.view.inputmethod.InputMethodManager;
74 import android.widget.Button;
75 import android.widget.EditText;
76 import android.widget.ImageView;
77 import android.widget.RelativeLayout;
78 import android.widget.TextView;
79 import android.widget.Toast;
80 import android.widget.ViewFlipper;
81 import de.mud.terminal.vt320;
82
83 public class ConsoleActivity extends Activity implements FileChooserCallback {
84 public final static String TAG = "ConnectBot.ConsoleActivity";
85
86 protected static final int REQUEST_EDIT = 1;
87
88 private static final int CLICK_TIME = 400;
89 private static final float MAX_CLICK_DISTANCE = 25f;
90 private static final int KEYBOARD_DISPLAY_TIME = 1500;
91
92 // Direction to shift the ViewFlipper
93 private static final int SHIFT_LEFT = 0;
94 private static final int SHIFT_RIGHT = 1;
95
96 protected ViewFlipper flip = null;
97 protected TerminalManager bound = null;
98 protected LayoutInflater inflater = null;
99
100 private SharedPreferences prefs = null;
101
102 // determines whether or not menuitem accelerators are bound
103 // otherwise they collide with an external keyboard's CTRL-char
104 private boolean hardKeyboard = false;
105
106 // determines whether we are in the fullscreen mode
107 private static final int FULLSCREEN_ON = 1;
108 private static final int FULLSCREEN_OFF = 2;
109
110 private int fullScreen;
111
112 protected Uri requested;
113
114 protected ClipboardManager clipboard;
115 private RelativeLayout stringPromptGroup;
116 protected EditText stringPrompt;
117 private TextView stringPromptInstructions;
118
119 private RelativeLayout booleanPromptGroup;
120 private TextView booleanPrompt;
121 private Button booleanYes, booleanNo;
122
123 private RelativeLayout keyboardGroup;
124 private Runnable keyboardGroupHider;
125
126 private TextView empty;
127
128 private Animation slide_left_in, slide_left_out, slide_right_in, slide_right_out, fade_stay_hidden, fade_out_delayed;
129
130 private Animation keyboard_fade_in, keyboard_fade_out;
131 private float lastX, lastY;
132
133 private InputMethodManager inputManager;
134
135 private MenuItem disconnect, copy, paste, portForward, resize, urlscan, screenCapture, download, upload;
136
137 protected TerminalBridge copySource = null;
138 private int lastTouchRow, lastTouchCol;
139
140 private boolean forcedOrientation;
141
142 private Handler handler = new Handler();
143
144 private ImageView mKeyboardButton;
145
146 private ActionBarWrapper actionBar;
147 private boolean inActionBarMenu = false;
148
149 private ServiceConnection connection = new ServiceConnection() {
150 public void onServiceConnected(ComponentName className, IBinder service) {
151 bound = ((TerminalManager.TerminalBinder) service).getService();
152 // let manager know about our event handling services
153 bound.disconnectHandler = disconnectHandler;
154 Log.d(TAG, String.format("Connected to TerminalManager and found bridges.size=%d", bound.bridges.size()));
155 bound.setResizeAllowed(true);
156 bound.hardKeyboardHidden = (getResources().getConfiguration().hardKeyboardHidden ==
157 Configuration.HARDKEYBOARDHIDDEN_YES);
158
159 // set fullscreen value
160 if (bound.getFullScreen() == 0) {
161 setFullScreen(FULLSCREEN_OFF);
162 }
163 else if (fullScreen != bound.getFullScreen())
164 setFullScreen(bound.getFullScreen());
165
166 // clear out any existing bridges and record requested index
167 flip.removeAllViews();
168 final String requestedNickname = (requested != null) ? requested.getFragment() : null;
169 int requestedIndex = -1;
170 TerminalBridge requestedBridge = bound.getConnectedBridge(requestedNickname);
171
172 // If we didn't find the requested connection, try opening it
173 if (requestedNickname != null && requestedBridge == null) {
174 try {
175 Log.d(TAG, String.format("We couldnt find an existing bridge with URI=%s (nickname=%s), so creating one now", requested.toString(), requestedNickname));
176 requestedBridge = bound.openConnection(requested);
177 }
178 catch (Exception e) {
179 Log.e(TAG, "Problem while trying to create new requested bridge from URI", e);
180 }
181 }
182
183 // create views for all bridges on this service
184 for (TerminalBridge bridge : bound.bridges) {
185 final int currentIndex = addNewTerminalView(bridge);
186
187 // check to see if this bridge was requested
188 if (bridge == requestedBridge) {
189 requestedIndex = currentIndex;
190 // store this bridge as default bridge
191 bound.defaultBridge = bridge;
192 }
193 }
194
195 // if no bridge was requested, try using default bridge
196 if (requestedIndex < 0) {
197 requestedIndex = getFlipIndex(bound.defaultBridge);
198
199 if (requestedIndex < 0)
200 requestedIndex = 0;
201 }
202
203 setDisplayedTerminal(requestedIndex);
204 }
205 public void onServiceDisconnected(ComponentName className) {
206 // tell each bridge to forget about our prompt handler
207 synchronized (bound.bridges) {
208 for (TerminalBridge bridge : bound.bridges)
209 bridge.promptHelper.setHandler(null);
210 }
211
212 flip.removeAllViews();
213 updateEmptyVisible();
214 bound = null;
215 }
216 };
217
218 protected Handler promptHandler = new Handler() {
219 @Override
220 public void handleMessage(Message msg) {
221 // someone below us requested to display a prompt
222 updatePromptVisible();
223 }
224 };
225
226 protected Handler disconnectHandler = new Handler() {
227 @Override
228 public void handleMessage(Message msg) {
229 Log.d(TAG, "Someone sending HANDLE_DISCONNECT to parentHandler");
230 // someone below us requested to display a password dialog
231 // they are sending nickname and requested
232 TerminalBridge bridge = (TerminalBridge)msg.obj;
233
234 if (bridge.isAwaitingClose())
235 closeBridge(bridge);
236 }
237 };
238
239 /**
240 * @param bridge
241 */
242 private void closeBridge(final TerminalBridge bridge) {
243 synchronized (flip) {
244 final int flipIndex = getFlipIndex(bridge);
245
246 if (flipIndex >= 0) {
247 if (flip.getDisplayedChild() == flipIndex) {
248 shiftCurrentTerminal(SHIFT_LEFT);
249 }
250
251 flip.removeViewAt(flipIndex);
252 /* TODO Remove this workaround when ViewFlipper is fixed to listen
253 * to view removals. Android Issue 1784
254 */
255 final int numChildren = flip.getChildCount();
256
257 if (flip.getDisplayedChild() >= numChildren &&
258 numChildren > 0) {
259 flip.setDisplayedChild(numChildren - 1);
260 }
261
262 updateEmptyVisible();
263 }
264
265 // If we just closed the last bridge, go back to the previous activity.
266 if (flip.getChildCount() == 0) {
267 finish();
268 }
269 }
270 }
271
272 protected View findCurrentView(int id) {
273 View view = flip.getCurrentView();
274
275 if (view == null) return null;
276
277 return view.findViewById(id);
278 }
279
280 protected PromptHelper getCurrentPromptHelper() {
281 View view = findCurrentView(R.id.console_flip);
282
283 if (!(view instanceof TerminalView)) return null;
284
285 return ((TerminalView)view).bridge.promptHelper;
286 }
287
288 protected void hideAllPrompts() {
289 stringPromptGroup.setVisibility(View.GONE);
290 booleanPromptGroup.setVisibility(View.GONE);
291 // adjust window back if size was changed during prompt input
292 View view = findCurrentView(R.id.console_flip);
293
294 if (!(view instanceof TerminalView)) return;
295
296 ((TerminalView)view).bridge.parentChanged((TerminalView)view);
297 }
298
299 private void showEmulatedKeys() {
300 keyboardGroup.startAnimation(keyboard_fade_in);
301 keyboardGroup.setVisibility(View.VISIBLE);
302 actionBar.show();
303
304 if (keyboardGroupHider != null)
305 handler.removeCallbacks(keyboardGroupHider);
306
307 keyboardGroupHider = new Runnable() {
308 public void run() {
309 if (keyboardGroup.getVisibility() == View.GONE || inActionBarMenu)
310 return;
311
312 keyboardGroup.startAnimation(keyboard_fade_out);
313 keyboardGroup.setVisibility(View.GONE);
314 actionBar.hide();
315 keyboardGroupHider = null;
316 }
317 };
318 handler.postDelayed(keyboardGroupHider, KEYBOARD_DISPLAY_TIME);
319 }
320
321 private void hideEmulatedKeys() {
322 if (keyboardGroupHider != null)
323 handler.removeCallbacks(keyboardGroupHider);
324
325 keyboardGroup.setVisibility(View.GONE);
326 actionBar.hide();
327 }
328
329 // more like configureLaxMode -- enable network IO on UI thread
330 private void configureStrictMode() {
331 try {
332 Class.forName("android.os.StrictMode");
333 StrictModeSetup.run();
334 }
335 catch (ClassNotFoundException e) {
336 }
337 }
338 @Override
339 public void onCreate(Bundle icicle) {
340 super.onCreate(icicle);
341 configureStrictMode();
342 hardKeyboard = getResources().getConfiguration().keyboard ==
343 Configuration.KEYBOARD_QWERTY;
344 this.setContentView(R.layout.act_console);
345 clipboard = (ClipboardManager)getSystemService(CLIPBOARD_SERVICE);
346 prefs = PreferenceManager.getDefaultSharedPreferences(this);
347 // TODO find proper way to disable volume key beep if it exists.
348 setVolumeControlStream(AudioManager.STREAM_MUSIC);
349 // handle requested console from incoming intent
350 requested = getIntent().getData();
351 inflater = LayoutInflater.from(this);
352 flip = (ViewFlipper)findViewById(R.id.console_flip);
353 empty = (TextView)findViewById(android.R.id.empty);
354 stringPromptGroup = (RelativeLayout) findViewById(R.id.console_password_group);
355 stringPromptInstructions = (TextView) findViewById(R.id.console_password_instructions);
356 stringPrompt = (EditText)findViewById(R.id.console_password);
357 stringPrompt.setOnKeyListener(new OnKeyListener() {
358 public boolean onKey(View v, int keyCode, KeyEvent event) {
359 if (event.getAction() == KeyEvent.ACTION_UP) return false;
360
361 if (keyCode != KeyEvent.KEYCODE_ENTER) return false;
362
363 // pass collected password down to current terminal
364 String value = stringPrompt.getText().toString();
365 PromptHelper helper = getCurrentPromptHelper();
366
367 if (helper == null) return false;
368
369 helper.setResponse(value);
370 // finally clear password for next user
371 stringPrompt.setText("");
372 updatePromptVisible();
373 return true;
374 }
375 });
376 booleanPromptGroup = (RelativeLayout) findViewById(R.id.console_boolean_group);
377 booleanPrompt = (TextView)findViewById(R.id.console_prompt);
378 booleanYes = (Button)findViewById(R.id.console_prompt_yes);
379 booleanYes.setOnClickListener(new OnClickListener() {
380 public void onClick(View v) {
381 PromptHelper helper = getCurrentPromptHelper();
382
383 if (helper == null) return;
384
385 helper.setResponse(Boolean.TRUE);
386 updatePromptVisible();
387 }
388 });
389 booleanNo = (Button)findViewById(R.id.console_prompt_no);
390 booleanNo.setOnClickListener(new OnClickListener() {
391 public void onClick(View v) {
392 PromptHelper helper = getCurrentPromptHelper();
393
394 if (helper == null) return;
395
396 helper.setResponse(Boolean.FALSE);
397 updatePromptVisible();
398 }
399 });
400 // preload animations for terminal switching
401 slide_left_in = AnimationUtils.loadAnimation(this, R.anim.slide_left_in);
402 slide_left_out = AnimationUtils.loadAnimation(this, R.anim.slide_left_out);
403 slide_right_in = AnimationUtils.loadAnimation(this, R.anim.slide_right_in);
404 slide_right_out = AnimationUtils.loadAnimation(this, R.anim.slide_right_out);
405 fade_out_delayed = AnimationUtils.loadAnimation(this, R.anim.fade_out_delayed);
406 fade_stay_hidden = AnimationUtils.loadAnimation(this, R.anim.fade_stay_hidden);
407 // Preload animation for keyboard button
408 keyboard_fade_in = AnimationUtils.loadAnimation(this, R.anim.keyboard_fade_in);
409 keyboard_fade_out = AnimationUtils.loadAnimation(this, R.anim.keyboard_fade_out);
410 inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
411 keyboardGroup = (RelativeLayout) findViewById(R.id.keyboard_group);
412 mKeyboardButton = (ImageView) findViewById(R.id.button_keyboard);
413 mKeyboardButton.setOnClickListener(new OnClickListener() {
414 public void onClick(View view) {
415 View flip = findCurrentView(R.id.console_flip);
416
417 if (flip == null)
418 return;
419
420 inputManager.showSoftInput(flip, InputMethodManager.SHOW_FORCED);
421 hideEmulatedKeys();
422 }
423 });
424 final ImageView symButton = (ImageView) findViewById(R.id.button_sym);
425 symButton.setOnClickListener(new OnClickListener() {
426 public void onClick(View view) {
427 View flip = findCurrentView(R.id.console_flip);
428
429 if (flip == null) return;
430
431 TerminalView terminal = (TerminalView)flip;
432 terminal.bridge.showCharPickerDialog();
433 keyboardGroup.setVisibility(View.GONE);
434 }
435 });
436 symButton.setOnLongClickListener(new OnLongClickListener() {
437 public boolean onLongClick(View view) {
438 View flip = findCurrentView(R.id.console_flip);
439
440 if (flip == null) return false;
441
442 TerminalView terminal = (TerminalView)flip;
443 terminal.bridge.showArrowsDialog();
444 return true;
445 }
446 });
447 final ImageView mInputButton = (ImageView) findViewById(R.id.button_input);
448 mInputButton.setOnClickListener(new OnClickListener() {
449 public void onClick(View view) {
450 View flip = findCurrentView(R.id.console_flip);
451
452 if (flip == null) return;
453
454 final TerminalView terminal = (TerminalView)flip;
455 Thread promptThread = new Thread(new Runnable() {
456 public void run() {
457 String inj = getCurrentPromptHelper().requestStringPrompt(null, "");
458 terminal.bridge.injectString(inj);
459 }
460 });
461 promptThread.setName("Prompt");
462 promptThread.setDaemon(true);
463 promptThread.start();
464 keyboardGroup.setVisibility(View.GONE);
465 }
466 });
467 final ImageView ctrlButton = (ImageView) findViewById(R.id.button_ctrl);
468 ctrlButton.setOnClickListener(new OnClickListener() {
469 public void onClick(View view) {
470 View flip = findCurrentView(R.id.console_flip);
471
472 if (flip == null) return;
473
474 TerminalView terminal = (TerminalView)flip;
475 TerminalKeyListener handler = terminal.bridge.getKeyHandler();
476 handler.metaPress(TerminalKeyListener.META_CTRL_ON);
477 hideEmulatedKeys();
478 }
479 });
480 ctrlButton.setOnLongClickListener(new OnLongClickListener() {
481 public boolean onLongClick(View view) {
482 View flip = findCurrentView(R.id.console_flip);
483
484 if (flip == null) return false;
485
486 TerminalView terminal = (TerminalView)flip;
487 terminal.bridge.showCtrlDialog();
488 return true;
489 }
490 });
491 final ImageView escButton = (ImageView) findViewById(R.id.button_esc);
492 escButton.setOnClickListener(new OnClickListener() {
493 public void onClick(View view) {
494 View flip = findCurrentView(R.id.console_flip);
495
496 if (flip == null) return;
497
498 TerminalView terminal = (TerminalView)flip;
499 TerminalKeyListener handler = terminal.bridge.getKeyHandler();
500 handler.sendEscape();
501 hideEmulatedKeys();
502 }
503 });
504 escButton.setOnLongClickListener(new OnLongClickListener() {
505 public boolean onLongClick(View view) {
506 View flip = findCurrentView(R.id.console_flip);
507
508 if (flip == null) return false;
509
510 TerminalView terminal = (TerminalView)flip;
511 terminal.bridge.showFKeysDialog();
512 return true;
513 }
514 });
515 actionBar = ActionBarWrapper.getActionBar(this);
516 actionBar.setDisplayHomeAsUpEnabled(true);
517 actionBar.hide();
518 actionBar.addOnMenuVisibilityListener(new ActionBarWrapper.OnMenuVisibilityListener() {
519 public void onMenuVisibilityChanged(boolean isVisible) {
520 inActionBarMenu = isVisible;
521
522 if (isVisible == false) {
523 hideEmulatedKeys();
524 }
525 }
526 });
527 // detect fling gestures to switch between terminals
528 final GestureDetector gestDetect = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
529 private float totalY = 0;
530 @Override
531 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
532 final float distx = e2.getRawX() - e1.getRawX();
533 final float disty = e2.getRawY() - e1.getRawY();
534 final int goalwidth = flip.getWidth() / 2;
535
536 // need to slide across half of display to trigger console change
537 // make sure user kept a steady hand horizontally
538 if (Math.abs(disty) < (flip.getHeight() / 4)) {
539 if (distx > goalwidth) {
540 shiftCurrentTerminal(SHIFT_RIGHT);
541 return true;
542 }
543
544 if (distx < -goalwidth) {
545 shiftCurrentTerminal(SHIFT_LEFT);
546 return true;
547 }
548 }
549
550 return false;
551 }
552 @Override
553 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
554 // if copying, then ignore
555 if (copySource != null && copySource.isSelectingForCopy())
556 return false;
557
558 if (e1 == null || e2 == null)
559 return false;
560
561 // if releasing then reset total scroll
562 if (e2.getAction() == MotionEvent.ACTION_UP) {
563 totalY = 0;
564 }
565
566 // activate consider if within x tolerance
567 if (Math.abs(e1.getX() - e2.getX()) < ViewConfiguration.getTouchSlop() * 4) {
568 View flip = findCurrentView(R.id.console_flip);
569
570 if (flip == null) return false;
571
572 TerminalView terminal = (TerminalView)flip;
573 // estimate how many rows we have scrolled through
574 // accumulate distance that doesn't trigger immediate scroll
575 totalY += distanceY;
576 final int moved = (int)(totalY / terminal.bridge.charHeight);
577
578 // consume as scrollback only if towards right half of screen
579 if (e2.getX() > flip.getWidth() / 2) {
580 if (moved != 0) {
581 int base = terminal.bridge.buffer.getWindowBase();
582 terminal.bridge.buffer.setWindowBase(base + moved);
583 totalY = 0;
584 return true;
585 }
586 }
587 else {
588 // otherwise consume as pgup/pgdown for every 5 lines
589 if (moved > 5) {
590 ((vt320)terminal.bridge.buffer).keyPressed(vt320.KEY_PAGE_DOWN, ' ', 0);
591 terminal.bridge.tryKeyVibrate();
592 totalY = 0;
593 return true;
594 }
595 else if (moved < -5) {
596 ((vt320)terminal.bridge.buffer).keyPressed(vt320.KEY_PAGE_UP, ' ', 0);
597 terminal.bridge.tryKeyVibrate();
598 totalY = 0;
599 return true;
600 }
601 }
602 }
603
604 return false;
605 }
606 /*
607 * Enables longpress and popups menu
608 *
609 * @see
610 * android.view.GestureDetector.SimpleOnGestureListener#
611 * onLongPress(android.view.MotionEvent)
612 *
613 * @return void
614 */
615 @Override
616 public void onLongPress(MotionEvent e) {
617 List<String> itemList = new ArrayList<String>();
618 final TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip);
619
620 if (terminalView == null)
621 return;
622
623 final TerminalBridge bridge = terminalView.bridge;
624
625 if (fullScreen == FULLSCREEN_ON)
626 itemList.add(ConsoleActivity.this
627 .getResources().getString(R.string.longpress_disable_full_screen_mode));
628 else
629 itemList.add(ConsoleActivity.this
630 .getResources().getString(R.string.longpress_enable_full_screen_mode));
631
632 itemList.add(ConsoleActivity.this
633 .getResources().getString(R.string.longpress_change_font_size));
634
635 if (prefs.getBoolean(PreferenceConstants.EXTENDED_LONGPRESS, false)) {
636 itemList.add(ConsoleActivity.this
637 .getResources().getString(R.string.longpress_arrows_dialog));
638 itemList.add(ConsoleActivity.this
639 .getResources().getString(R.string.longpress_fkeys_dialog));
640 itemList.add(ConsoleActivity.this
641 .getResources().getString(R.string.longpress_ctrl_dialog));
642 itemList.add(ConsoleActivity.this
643 .getResources().getString(R.string.longpress_sym_dialog));
644 }
645
646 if (itemList.size() > 0) {
647 AlertDialog.Builder builder = new AlertDialog.Builder(ConsoleActivity.this);
648 builder.setTitle(R.string.longpress_select_action);
649 builder.setItems(itemList.toArray(new CharSequence[itemList.size()]),
650 new DialogInterface.OnClickListener() {
651 public void onClick(DialogInterface dialog, int item) {
652 switch (item) {
653 case 0:
654 if (fullScreen == FULLSCREEN_ON) {
655 setFullScreen(FULLSCREEN_OFF);
656 }
657 else
658 setFullScreen(FULLSCREEN_ON);
659
660 break;
661
662 case 1:
663 bridge.showFontSizeDialog();
664 break;
665
666 case 2:
667 bridge.showArrowsDialog();
668 break;
669
670 case 3:
671 bridge.showFKeysDialog();
672 break;
673
674 case 4:
675 bridge.showCtrlDialog();
676 break;
677
678 case 5:
679 bridge.showCharPickerDialog();
680 }
681 }
682 });
683 AlertDialog alert = builder.create();
684 alert.show();
685 }
686 }
687 });
688 flip.setLongClickable(true);
689 flip.setOnTouchListener(new OnTouchListener() {
690 public boolean onTouch(View v, MotionEvent event) {
691 // when copying, highlight the area
692 if (copySource != null && copySource.isSelectingForCopy()) {
693 int row = (int)FloatMath.floor(event.getY() / copySource.charHeight);
694 int col = (int)FloatMath.floor(event.getX() / copySource.charWidth);
695 SelectionArea area = copySource.getSelectionArea();
696
697 switch (event.getAction()) {
698 case MotionEvent.ACTION_DOWN:
699
700 // recording starting area
701 if (area.isSelectingOrigin()) {
702 area.setRow(row);
703 area.setColumn(col);
704 lastTouchRow = row;
705 lastTouchCol = col;
706 copySource.redraw();
707 }
708
709 return true;
710
711 case MotionEvent.ACTION_MOVE:
712
713 /* ignore when user hasn't moved since last time so
714 * we can fine-tune with directional pad
715 */
716 if (row == lastTouchRow && col == lastTouchCol)
717 return true;
718
719 // if the user moves, start the selection for other corner
720 area.finishSelectingOrigin();
721 // update selected area
722 area.setRow(row);
723 area.setColumn(col);
724 lastTouchRow = row;
725 lastTouchCol = col;
726 copySource.redraw();
727 return true;
728
729 case MotionEvent.ACTION_UP:
730
731 /* If they didn't move their finger, maybe they meant to
732 * select the rest of the text with the directional pad.
733 */
734 if (area.getLeft() == area.getRight() &&
735 area.getTop() == area.getBottom()) {
736 return true;
737 }
738
739 // copy selected area to clipboard
740 String copiedText = area.copyFrom(copySource.buffer);
741 clipboard.setText(copiedText);
742 Toast.makeText(ConsoleActivity.this, getString(R.string.console_copy_done, copiedText.length()), Toast.LENGTH_LONG).show();
743
744 // fall through to clear state
745 case MotionEvent.ACTION_CANCEL:
746 // make sure we clear any highlighted area
747 area.reset();
748 copySource.setSelectingForCopy(false);
749 copySource.redraw();
750 return true;
751 }
752 }
753
754 Configuration config = getResources().getConfiguration();
755
756 if (event.getAction() == MotionEvent.ACTION_DOWN) {
757 lastX = event.getX();
758 lastY = event.getY();
759 }
760 else if (event.getAction() == MotionEvent.ACTION_UP
761 && keyboardGroup.getVisibility() == View.GONE
762 && event.getEventTime() - event.getDownTime() < CLICK_TIME
763 && Math.abs(event.getX() - lastX) < MAX_CLICK_DISTANCE
764 && Math.abs(event.getY() - lastY) < MAX_CLICK_DISTANCE) {
765 showEmulatedKeys();
766 }
767
768 // pass any touch events back to detector
769 TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip);
770 return terminalView.mScaleDetector.onTouchEvent(event) | gestDetect.onTouchEvent(event);
771 }
772 });
773 }
774
775 /**
776 *
777 */
778 private void configureOrientation() {
779 String rotateDefault;
780
781 if (getResources().getConfiguration().keyboard == Configuration.KEYBOARD_NOKEYS)
782 rotateDefault = PreferenceConstants.ROTATION_PORTRAIT;
783 else
784 rotateDefault = PreferenceConstants.ROTATION_LANDSCAPE;
785
786 String rotate = prefs.getString(PreferenceConstants.ROTATION, rotateDefault);
787
788 if (PreferenceConstants.ROTATION_DEFAULT.equals(rotate))
789 rotate = rotateDefault;
790
791 // request a forced orientation if requested by user
792 if (PreferenceConstants.ROTATION_LANDSCAPE.equals(rotate)) {
793 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
794 forcedOrientation = true;
795 }
796 else if (PreferenceConstants.ROTATION_PORTRAIT.equals(rotate)) {
797 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
798 forcedOrientation = true;
799 }
800 else {
801 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
802 forcedOrientation = false;
803 }
804 }
805
806
807 @Override
808 public boolean onCreateOptionsMenu(Menu menu) {
809 super.onCreateOptionsMenu(menu);
810 View view = findCurrentView(R.id.console_flip);
811 final boolean activeTerminal = (view instanceof TerminalView);
812 boolean sessionOpen = false;
813 boolean disconnected = false;
814 boolean canForwardPorts = false;
815 boolean canTransferFiles = false;
816
817 if (activeTerminal) {
818 TerminalBridge bridge = ((TerminalView) view).bridge;
819 sessionOpen = bridge.isSessionOpen();
820 disconnected = bridge.isDisconnected();
821 canForwardPorts = bridge.canFowardPorts();
822 canTransferFiles = bridge.canTransferFiles();
823 }
824
825 menu.setQwertyMode(true);
826
827 if (!PreferenceConstants.PRE_HONEYCOMB) {
828 MenuItem ctrlKey = menu.add(getString(R.string.fullscreen));
829 ctrlKey.setEnabled(activeTerminal);
830 ctrlKey.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
831 ctrlKey.setOnMenuItemClickListener(new OnMenuItemClickListener() {
832 public boolean onMenuItemClick(MenuItem menuItem) {
833 if (fullScreen == FULLSCREEN_ON) {
834 setFullScreen(FULLSCREEN_OFF);
835 }
836 else
837 setFullScreen(FULLSCREEN_ON);
838
839 return true;
840 }
841 });
842 }
843
844 disconnect = menu.add(R.string.list_host_disconnect);
845
846 if (hardKeyboard)
847 disconnect.setAlphabeticShortcut('w');
848
849 if (!sessionOpen && disconnected)
850 disconnect.setTitle(R.string.console_menu_close);
851
852 disconnect.setEnabled(activeTerminal);
853 disconnect.setIcon(android.R.drawable.ic_menu_close_clear_cancel);
854 disconnect.setOnMenuItemClickListener(new OnMenuItemClickListener() {
855 public boolean onMenuItemClick(MenuItem item) {
856 // disconnect or close the currently visible session
857 TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip);
858 TerminalBridge bridge = terminalView.bridge;
859 bridge.dispatchDisconnect(true);
860 return true;
861 }
862 });
863 copy = menu.add(R.string.console_menu_copy);
864
865 if (hardKeyboard)
866 copy.setAlphabeticShortcut('c');
867
868 copy.setIcon(android.R.drawable.ic_menu_set_as);
869 copy.setEnabled(activeTerminal);
870 copy.setOnMenuItemClickListener(new OnMenuItemClickListener() {
871 public boolean onMenuItemClick(MenuItem item) {
872 // mark as copying and reset any previous bounds
873 TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip);
874 copySource = terminalView.bridge;
875 SelectionArea area = copySource.getSelectionArea();
876 area.reset();
877 area.setBounds(copySource.buffer.getColumns(), copySource.buffer.getRows());
878 copySource.setSelectingForCopy(true);
879 // Make sure we show the initial selection
880 copySource.redraw();
881 Toast.makeText(ConsoleActivity.this, getString(R.string.console_copy_start), Toast.LENGTH_LONG).show();
882 return true;
883 }
884 });
885 paste = menu.add(R.string.console_menu_paste);
886
887 if (hardKeyboard)
888 paste.setAlphabeticShortcut('v');
889
890 paste.setIcon(android.R.drawable.ic_menu_edit);
891 paste.setEnabled(clipboard.hasText() && sessionOpen);
892 paste.setOnMenuItemClickListener(new OnMenuItemClickListener() {
893 public boolean onMenuItemClick(MenuItem item) {
894 // force insert of clipboard text into current console
895 TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip);
896 TerminalBridge bridge = terminalView.bridge;
897 // pull string from clipboard and generate all events to force down
898 String clip = clipboard.getText().toString();
899 bridge.injectString(clip);
900 return true;
901 }
902 });
903 resize = menu.add(R.string.console_menu_resize);
904
905 if (hardKeyboard)
906 resize.setAlphabeticShortcut('r');
907
908 resize.setIcon(android.R.drawable.ic_menu_crop);
909 resize.setEnabled(sessionOpen);
910 resize.setOnMenuItemClickListener(new OnMenuItemClickListener() {
911 public boolean onMenuItemClick(MenuItem item) {
912 final TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip);
913 final View resizeView = inflater.inflate(R.layout.dia_resize, null, false);
914 ((EditText) resizeView.findViewById(R.id.width))
915 .setText(prefs.getString(PreferenceConstants.DEFAULT_FONT_SIZE_WIDTH, "80"));
916 ((EditText) resizeView.findViewById(R.id.height))
917 .setText(prefs.getString(PreferenceConstants.DEFAULT_FONT_SIZE_HEIGHT, "25"));
918 new AlertDialog.Builder(ConsoleActivity.this)
919 .setView(resizeView)
920 .setPositiveButton(R.string.button_resize, new DialogInterface.OnClickListener() {
921 public void onClick(DialogInterface dialog, int which) {
922 int width, height;
923
924 try {
925 width = Integer.parseInt(((EditText) resizeView
926 .findViewById(R.id.width))
927 .getText().toString());
928 height = Integer.parseInt(((EditText) resizeView
929 .findViewById(R.id.height))
930 .getText().toString());
931 }
932 catch (NumberFormatException nfe) {
933 // TODO change this to a real dialog where we can
934 // make the input boxes turn red to indicate an error.
935 return;
936 }
937
938 if (width > 0 && height > 0) {
939 terminalView.forceSize(width, height);
940 }
941 else {
942 new AlertDialog.Builder(ConsoleActivity.this)
943 .setTitle(R.string.resize_error_title)
944 .setMessage(R.string.resize_error_width_height)
945 .setNegativeButton(R.string.button_close, null)
946 .show();
947 }
948 }
949 }).setNeutralButton(R.string.button_resize_reset, new DialogInterface.OnClickListener() {
950 public void onClick(DialogInterface dialog, int which) {
951 terminalView.bridge.resetSize(terminalView);
952 }
953 }).setNegativeButton(android.R.string.cancel, null)
954 .create().show();
955 return true;
956 }
957 });
958 screenCapture = menu.add(R.string.console_menu_screencapture);
959
960 if (hardKeyboard)
961 screenCapture.setAlphabeticShortcut('s');
962
963 screenCapture.setIcon(android.R.drawable.ic_menu_camera);
964 screenCapture.setEnabled(activeTerminal);
965 screenCapture.setOnMenuItemClickListener(new OnMenuItemClickListener() {
966 public boolean onMenuItemClick(MenuItem item) {
967 final TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip);
968 terminalView.bridge.captureScreen();
969 return true;
970 }
971 });
972 portForward = menu.add(R.string.console_menu_portforwards);
973
974 if (hardKeyboard)
975 portForward.setAlphabeticShortcut('f');
976
977 portForward.setIcon(android.R.drawable.ic_menu_manage);
978 portForward.setEnabled(sessionOpen && canForwardPorts);
979 portForward.setOnMenuItemClickListener(new OnMenuItemClickListener() {
980 public boolean onMenuItemClick(MenuItem item) {
981 TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip);
982 TerminalBridge bridge = terminalView.bridge;
983 Intent intent = new Intent(ConsoleActivity.this, PortForwardListActivity.class);
984 intent.putExtra(Intent.EXTRA_TITLE, bridge.host.getId());
985 ConsoleActivity.this.startActivityForResult(intent, REQUEST_EDIT);
986 return true;
987 }
988 });
989 urlscan = menu.add(R.string.console_menu_urlscan);
990
991 if (hardKeyboard)
992 urlscan.setAlphabeticShortcut('l');
993
994 urlscan.setIcon(android.R.drawable.ic_menu_search);
995 urlscan.setEnabled(activeTerminal);
996 urlscan.setOnMenuItemClickListener(new OnMenuItemClickListener() {
997 public boolean onMenuItemClick(MenuItem item) {
998 View flip = findCurrentView(R.id.console_flip);
999
1000 if (flip == null) return true;
1001
1002 TerminalView terminal = (TerminalView)flip;
1003 TerminalKeyListener handler = terminal.bridge.getKeyHandler();
1004 handler.urlScan(terminal);
1005 return true;
1006 }
1007 });
1008 download = menu.add(R.string.console_menu_download);
1009 download.setAlphabeticShortcut('d');
1010 download.setEnabled(sessionOpen && canTransferFiles);
1011 download.setOnMenuItemClickListener(new OnMenuItemClickListener() {
1012 public boolean onMenuItemClick(MenuItem item) {
1013 final String downloadFolder = prefs.getString(PreferenceConstants.DOWNLOAD_FOLDER, "");
1014 final TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip);
1015 final TerminalBridge bridge = terminalView.bridge;
1016 final EditText textField = new EditText(ConsoleActivity.this);
1017 new AlertDialog.Builder(ConsoleActivity.this)
1018 .setTitle(R.string.transfer_select_remote_download_title)
1019 .setMessage(R.string.transfer_select_remote_download_desc)
1020 .setView(textField)
1021 .setPositiveButton(R.string.transfer_button_download, new DialogInterface.OnClickListener() {
1022 public void onClick(DialogInterface dialog, int whichButton) {
1023 TransferThread transfer = new TransferThread(ConsoleActivity.this, handler);
1024
1025 if (!prefs.getBoolean(PreferenceConstants.BACKGROUND_FILE_TRANSFER, true))
1026 transfer.setProgressDialogMessage(getString(R.string.transfer_downloading));
1027
1028 transfer.download(bridge, textField.getText().toString(), null, downloadFolder);
1029 }
1030 }).setNegativeButton(android.R.string.cancel, null).create().show();
1031 return true;
1032 }
1033 });
1034 upload = menu.add(R.string.console_menu_upload);
1035 upload.setAlphabeticShortcut('u');
1036 upload.setEnabled(sessionOpen && canTransferFiles);
1037 upload.setOnMenuItemClickListener(new OnMenuItemClickListener() {
1038 public boolean onMenuItemClick(MenuItem item) {
1039 FileChooser.selectFile(ConsoleActivity.this, ConsoleActivity.this,
1040 FileChooser.REQUEST_CODE_SELECT_FILE,
1041 getString(R.string.file_chooser_select_file, getString(R.string.select_for_upload)));
1042 return true;
1043 }
1044 });
1045 return true;
1046 }
1047
1048 @Override
1049 protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
1050 super.onActivityResult(requestCode, resultCode, intent);
1051
1052 switch (requestCode) {
1053 case FileChooser.REQUEST_CODE_SELECT_FILE:
1054 if (resultCode == RESULT_OK && intent != null) {
1055 Uri uri = intent.getData();
1056
1057 try {
1058 if (uri != null) {
1059 fileSelected(new File(URI.create(uri.toString())));
1060 }
1061 else {
1062 String filename = intent.getDataString();
1063
1064 if (filename != null) {
1065 fileSelected(new File(URI.create(filename)));
1066 }
1067 }
1068 }
1069 catch (IllegalArgumentException e) {
1070 Log.e(TAG, "Couldn't read from selected file", e);
1071 }
1072 }
1073
1074 break;
1075 }
1076 }
1077
1078 public void fileSelected(final File f) {
1079 String destFileName;
1080 String uploadFolder = prefs.getString(PreferenceConstants.REMOTE_UPLOAD_FOLDER, null);
1081 final TransferThread transfer = new TransferThread(ConsoleActivity.this, handler);
1082 Log.d(TAG, "File chooser returned " + f);
1083
1084 if (uploadFolder == null)
1085 uploadFolder = "";
1086
1087 if (!uploadFolder.equals("") && uploadFolder.charAt(uploadFolder.length() - 1) != '/')
1088 destFileName = uploadFolder + "/" + f.getName();
1089 else
1090 destFileName = uploadFolder + f.getName();
1091
1092 if (prefs.getBoolean(PreferenceConstants.UPLOAD_DESTINATION_PROMPT, true)) {
1093 final EditText fileDest = new EditText(ConsoleActivity.this);
1094 fileDest.setSingleLine();
1095 fileDest.setText(destFileName);
1096 new AlertDialog.Builder(ConsoleActivity.this)
1097 .setTitle(R.string.transfer_select_remote_upload_dest_title)
1098 .setMessage(getResources().getString(R.string.transfer_select_remote_upload_dest_desc) + "\n" + f.getPath())
1099 .setView(fileDest)
1100 .setPositiveButton(R.string.transfer_button_upload, new DialogInterface.OnClickListener() {
1101 public void onClick(DialogInterface dialog, int whichButton) {
1102 if (!prefs.getBoolean(PreferenceConstants.BACKGROUND_FILE_TRANSFER, true))
1103 transfer.setProgressDialogMessage(getString(R.string.transfer_uploading));
1104
1105 TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip);
1106 TerminalBridge bridge = terminalView.bridge;
1107 File uf = new File(fileDest.getText().toString());
1108 String name = "", parent = "";
1109
1110 if (uf.getParent() != null)
1111 parent = uf.getParent().toString();
1112
1113 if (uf.getName() != null)
1114 name = uf.getName().toString();
1115
1116 transfer.upload(bridge, f.toString(), name, parent);
1117 }
1118 }).setNegativeButton(android.R.string.cancel, null).create().show();
1119 }
1120 else {
1121 if (!prefs.getBoolean(PreferenceConstants.BACKGROUND_FILE_TRANSFER, true))
1122 transfer.setProgressDialogMessage(getString(R.string.transfer_uploading));
1123
1124 TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip);
1125 TerminalBridge bridge = terminalView.bridge;
1126 transfer.upload(bridge, f.toString(), null, uploadFolder);
1127 }
1128 }
1129
1130 @Override
1131 public boolean onPrepareOptionsMenu(Menu menu) {
1132 super.onPrepareOptionsMenu(menu);
1133 setVolumeControlStream(AudioManager.STREAM_NOTIFICATION);
1134 final View view = findCurrentView(R.id.console_flip);
1135 boolean activeTerminal = (view instanceof TerminalView);
1136 boolean sessionOpen = false;
1137 boolean disconnected = false;
1138 boolean canForwardPorts = false;
1139 boolean canTransferFiles = false;
1140
1141 if (activeTerminal) {
1142 TerminalBridge bridge = ((TerminalView) view).bridge;
1143 sessionOpen = bridge.isSessionOpen();
1144 disconnected = bridge.isDisconnected();
1145 canForwardPorts = bridge.canFowardPorts();
1146 canTransferFiles = bridge.canTransferFiles();
1147 }
1148
1149 disconnect.setEnabled(activeTerminal);
1150
1151 if (sessionOpen || !disconnected)
1152 disconnect.setTitle(R.string.list_host_disconnect);
1153 else
1154 disconnect.setTitle(R.string.console_menu_close);
1155
1156 copy.setEnabled(activeTerminal);
1157 paste.setEnabled(clipboard.hasText() && sessionOpen);
1158 portForward.setEnabled(sessionOpen && canForwardPorts);
1159 urlscan.setEnabled(activeTerminal);
1160 resize.setEnabled(sessionOpen);
1161 download.setEnabled(sessionOpen && canTransferFiles);
1162 upload.setEnabled(sessionOpen && canTransferFiles);
1163 return true;
1164 }
1165
1166 @Override
1167 public boolean onOptionsItemSelected(MenuItem item) {
1168 switch (item.getItemId()) {
1169 case android.R.id.home:
1170 Intent intent = new Intent(this, HostListActivity.class);
1171 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
1172 startActivity(intent);
1173 return true;
1174
1175 default:
1176 return super.onOptionsItemSelected(item);
1177 }
1178 }
1179
1180 @Override
1181 public void onOptionsMenuClosed(Menu menu) {
1182 super.onOptionsMenuClosed(menu);
1183 setVolumeControlStream(AudioManager.STREAM_MUSIC);
1184 }
1185
1186 @Override
1187 public void onStart() {
1188 super.onStart();
1189 // connect with manager service to find all bridges
1190 // when connected it will insert all views
1191 bindService(new Intent(this, TerminalManager.class), connection, Context.BIND_AUTO_CREATE);
1192
1193 if (getResources().getConfiguration().hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
1194 this.mKeyboardButton.setVisibility(View.GONE);
1195 }
1196 }
1197
1198 @Override
1199 public void onPause() {
1200 super.onPause();
1201 Log.d(TAG, "onPause called");
1202
1203 if (forcedOrientation && bound != null)
1204 bound.setResizeAllowed(false);
1205 }
1206
1207 @Override
1208 public void onResume() {
1209 super.onResume();
1210 Log.d(TAG, "onResume called");
1211
1212 // Make sure we don't let the screen fall asleep.
1213 // This also keeps the Wi-Fi chipset from disconnecting us.
1214 if (prefs.getBoolean(PreferenceConstants.KEEP_ALIVE, true)) {
1215 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1216 }
1217 else {
1218 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1219 }
1220
1221 configureOrientation();
1222
1223 if (forcedOrientation && bound != null)
1224 bound.setResizeAllowed(true);
1225 }
1226
1227 /* (non-Javadoc)
1228 * @see android.app.Activity#onNewIntent(android.content.Intent)
1229 */
1230 @Override
1231 protected void onNewIntent(Intent intent) {
1232 super.onNewIntent(intent);
1233 Log.d(TAG, "onNewIntent called");
1234 requested = intent.getData();
1235
1236 if (requested == null) {
1237 Log.e(TAG, "Got null intent data in onNewIntent()");
1238 return;
1239 }
1240
1241 if (bound == null) {
1242 Log.e(TAG, "We're not bound in onNewIntent()");
1243 return;
1244 }
1245
1246 TerminalBridge requestedBridge = bound.getConnectedBridge(requested.getFragment());
1247 int requestedIndex = 0;
1248
1249 synchronized (flip) {
1250 if (requestedBridge == null) {
1251 // If we didn't find the requested connection, try opening it
1252 try {
1253 Log.d(TAG, String.format("We couldnt find an existing bridge with URI=%s (nickname=%s)," +
1254 "so creating one now", requested.toString(), requested.getFragment()));
1255 requestedBridge = bound.openConnection(requested);
1256 }
1257 catch (Exception e) {
1258 Log.e(TAG, "Problem while trying to create new requested bridge from URI", e);
1259 // TODO: We should display an error dialog here.
1260 return;
1261 }
1262
1263 requestedIndex = addNewTerminalView(requestedBridge);
1264 }
1265 else {
1266 final int flipIndex = getFlipIndex(requestedBridge);
1267
1268 if (flipIndex > requestedIndex) {
1269 requestedIndex = flipIndex;
1270 }
1271 }
1272
1273 setDisplayedTerminal(requestedIndex);
1274 }
1275 }
1276
1277 @Override
1278 public void onStop() {
1279 super.onStop();
1280 unbindService(connection);
1281 }
1282
1283 protected void shiftCurrentTerminal(final int direction) {
1284 View overlay;
1285
1286 synchronized (flip) {
1287 boolean shouldAnimate = flip.getChildCount() > 1;
1288
1289 // Only show animation if there is something else to go to.
1290 if (shouldAnimate) {
1291 // keep current overlay from popping up again
1292 overlay = findCurrentView(R.id.terminal_overlay);
1293
1294 if (overlay != null)
1295 overlay.startAnimation(fade_stay_hidden);
1296
1297 if (direction == SHIFT_LEFT) {
1298 flip.setInAnimation(slide_left_in);
1299 flip.setOutAnimation(slide_left_out);
1300 flip.showNext();
1301 }
1302 else if (direction == SHIFT_RIGHT) {
1303 flip.setInAnimation(slide_right_in);
1304 flip.setOutAnimation(slide_right_out);
1305 flip.showPrevious();
1306 }
1307 }
1308
1309 updateDefault();
1310
1311 if (shouldAnimate) {
1312 // show overlay on new slide and start fade
1313 overlay = findCurrentView(R.id.terminal_overlay);
1314
1315 if (overlay != null)
1316 overlay.startAnimation(fade_out_delayed);
1317 }
1318
1319 updatePromptVisible();
1320 }
1321 }
1322
1323 /**
1324 * Save the currently shown {@link TerminalView} as the default. This is
1325 * saved back down into {@link TerminalManager} where we can read it again
1326 * later.
1327 */
1328 private void updateDefault() {
1329 // update the current default terminal
1330 View view = findCurrentView(R.id.console_flip);
1331
1332 if (!(view instanceof TerminalView)) return;
1333
1334 TerminalView terminal = (TerminalView)view;
1335
1336 if (bound != null) bound.defaultBridge = terminal.bridge;
1337
1338 // tell the bridge monitor it has the topmost visible window now.
1339 if (terminal.bridge.monitor != null) terminal.bridge.monitor.activate();
1340 }
1341
1342 protected void updateEmptyVisible() {
1343 // update visibility of empty status message
1344 empty.setVisibility((flip.getChildCount() == 0) ? View.VISIBLE : View.GONE);
1345 }
1346
1347 /**
1348 * Show any prompts requested by the currently visible {@link TerminalView}.
1349 */
1350 protected void updatePromptVisible() {
1351 // check if our currently-visible terminalbridge is requesting any prompt services
1352 View view = findCurrentView(R.id.console_flip);
1353 // Hide all the prompts in case a prompt request was canceled
1354 hideAllPrompts();
1355
1356 if (!(view instanceof TerminalView)) {
1357 // we dont have an active view, so hide any prompts
1358 return;
1359 }
1360
1361 PromptHelper prompt = ((TerminalView)view).bridge.promptHelper;
1362
1363 if (String.class.equals(prompt.promptRequested)) {
1364 stringPromptGroup.setVisibility(View.VISIBLE);
1365 String instructions = prompt.promptInstructions;
1366 boolean password = prompt.passwordRequested;
1367
1368 if (instructions != null && instructions.length() > 0) {
1369 stringPromptInstructions.setVisibility(View.VISIBLE);
1370 stringPromptInstructions.setText(instructions);
1371 }
1372 else
1373 stringPromptInstructions.setVisibility(View.GONE);
1374
1375 if (password) {
1376 stringPrompt.setInputType(InputType.TYPE_CLASS_TEXT |
1377 InputType.TYPE_TEXT_VARIATION_PASSWORD);
1378 stringPrompt.setTransformationMethod(PasswordTransformationMethod.getInstance());
1379 }
1380 else {
1381 stringPrompt.setInputType(InputType.TYPE_CLASS_TEXT |
1382 InputType.TYPE_TEXT_FLAG_AUTO_CORRECT);
1383 stringPrompt.setTransformationMethod(SingleLineTransformationMethod.getInstance());
1384 }
1385
1386 stringPrompt.setText("");
1387 stringPrompt.setHint(prompt.promptHint);
1388 stringPrompt.requestFocus();
1389 }
1390 else if (Boolean.class.equals(prompt.promptRequested)) {
1391 booleanPromptGroup.setVisibility(View.VISIBLE);
1392 booleanPrompt.setText(prompt.promptHint);
1393 booleanYes.requestFocus();
1394 }
1395 else {
1396 hideAllPrompts();
1397 view.requestFocus();
1398 }
1399 }
1400
1401 @Override
1402 public void onConfigurationChanged(Configuration newConfig) {
1403 super.onConfigurationChanged(newConfig);
1404 Log.d(TAG, String.format("onConfigurationChanged; requestedOrientation=%d, newConfig.orientation=%d", getRequestedOrientation(), newConfig.orientation));
1405
1406 if (bound != null) {
1407 if (forcedOrientation &&
1408 (newConfig.orientation != Configuration.ORIENTATION_LANDSCAPE &&
1409 getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) ||
1410 (newConfig.orientation != Configuration.ORIENTATION_PORTRAIT &&
1411 getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT))
1412 bound.setResizeAllowed(false);
1413 else
1414 bound.setResizeAllowed(true);
1415
1416 bound.hardKeyboardHidden = (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES);
1417 mKeyboardButton.setVisibility(bound.hardKeyboardHidden ? View.VISIBLE : View.GONE);
1418 }
1419 }
1420
1421 /**
1422 * Adds a new TerminalBridge to the current set of views in our ViewFlipper.
1423 *
1424 * @param bridge TerminalBridge to add to our ViewFlipper
1425 * @return the child index of the new view in the ViewFlipper
1426 */
1427 private int addNewTerminalView(TerminalBridge bridge) {
1428 // let them know about our prompt handler services
1429 bridge.promptHelper.setHandler(promptHandler);
1430 // inflate each terminal view
1431 RelativeLayout view = (RelativeLayout)inflater.inflate(R.layout.item_terminal, flip, false);
1432 // set the terminal overlay text
1433 TextView overlay = (TextView)view.findViewById(R.id.terminal_overlay);
1434 overlay.setText(bridge.host.getNickname());
1435 // and add our terminal view control, using index to place behind overlay
1436 TerminalView terminal = new TerminalView(ConsoleActivity.this, bridge);
1437 terminal.setId(R.id.console_flip);
1438 view.addView(terminal, 0);
1439
1440 synchronized (flip) {
1441 // finally attach to the flipper
1442 flip.addView(view);
1443 return flip.getChildCount() - 1;
1444 }
1445 }
1446
1447 private int getFlipIndex(TerminalBridge bridge) {
1448 synchronized (flip) {
1449 final int children = flip.getChildCount();
1450
1451 for (int i = 0; i < children; i++) {
1452 final View view = flip.getChildAt(i).findViewById(R.id.console_flip);
1453
1454 if (view == null || !(view instanceof TerminalView)) {
1455 // How did that happen?
1456 continue;
1457 }
1458
1459 final TerminalView tv = (TerminalView) view;
1460
1461 if (tv.bridge == bridge) {
1462 return i;
1463 }
1464 }
1465 }
1466
1467 return -1;
1468 }
1469
1470 /**
1471 * Displays the child in the ViewFlipper at the requestedIndex and updates the prompts.
1472 *
1473 * @param requestedIndex the index of the terminal view to display
1474 */
1475 private void setDisplayedTerminal(int requestedIndex) {
1476 synchronized (flip) {
1477 try {
1478 // show the requested bridge if found, also fade out overlay
1479 flip.setDisplayedChild(requestedIndex);
1480 flip.getCurrentView().findViewById(R.id.terminal_overlay)
1481 .startAnimation(fade_out_delayed);
1482 }
1483 catch (NullPointerException npe) {
1484 Log.d(TAG, "View went away when we were about to display it", npe);
1485 }
1486
1487 updatePromptVisible();
1488 updateEmptyVisible();
1489 }
1490 }
1491
1492 private void setFullScreen(int fullScreen) {
1493 if (fullScreen != this.fullScreen) {
1494 if (fullScreen == FULLSCREEN_ON) {
1495 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
1496 WindowManager.LayoutParams.FLAG_FULLSCREEN);
1497 }
1498 else {
1499 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
1500 }
1501
1502 this.fullScreen = fullScreen;
1503
1504 if (bound != null)
1505 bound.setFullScreen(this.fullScreen);
1506 }
1507 }
1508 }