0
+ − 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
263
c8d9ef252b23
menu force-size does not need a session, just an active terminal
Carl Byington <carl@five-ten-sg.com>
diff
changeset
+ − 620 if (terminalView == null) return;
0
+ − 621
+ − 622 final TerminalBridge bridge = terminalView.bridge;
+ − 623
+ − 624 if (fullScreen == FULLSCREEN_ON)
+ − 625 itemList.add(ConsoleActivity.this
+ − 626 .getResources().getString(R.string.longpress_disable_full_screen_mode));
+ − 627 else
+ − 628 itemList.add(ConsoleActivity.this
+ − 629 .getResources().getString(R.string.longpress_enable_full_screen_mode));
+ − 630
+ − 631 itemList.add(ConsoleActivity.this
+ − 632 .getResources().getString(R.string.longpress_change_font_size));
+ − 633
+ − 634 if (prefs.getBoolean(PreferenceConstants.EXTENDED_LONGPRESS, false)) {
+ − 635 itemList.add(ConsoleActivity.this
+ − 636 .getResources().getString(R.string.longpress_arrows_dialog));
+ − 637 itemList.add(ConsoleActivity.this
+ − 638 .getResources().getString(R.string.longpress_fkeys_dialog));
+ − 639 itemList.add(ConsoleActivity.this
+ − 640 .getResources().getString(R.string.longpress_ctrl_dialog));
+ − 641 itemList.add(ConsoleActivity.this
+ − 642 .getResources().getString(R.string.longpress_sym_dialog));
+ − 643 }
+ − 644
+ − 645 if (itemList.size() > 0) {
+ − 646 AlertDialog.Builder builder = new AlertDialog.Builder(ConsoleActivity.this);
+ − 647 builder.setTitle(R.string.longpress_select_action);
+ − 648 builder.setItems(itemList.toArray(new CharSequence[itemList.size()]),
+ − 649 new DialogInterface.OnClickListener() {
+ − 650 public void onClick(DialogInterface dialog, int item) {
+ − 651 switch (item) {
+ − 652 case 0:
+ − 653 if (fullScreen == FULLSCREEN_ON) {
+ − 654 setFullScreen(FULLSCREEN_OFF);
+ − 655 }
+ − 656 else
+ − 657 setFullScreen(FULLSCREEN_ON);
+ − 658
+ − 659 break;
+ − 660
+ − 661 case 1:
+ − 662 bridge.showFontSizeDialog();
+ − 663 break;
+ − 664
+ − 665 case 2:
+ − 666 bridge.showArrowsDialog();
+ − 667 break;
+ − 668
+ − 669 case 3:
+ − 670 bridge.showFKeysDialog();
+ − 671 break;
+ − 672
+ − 673 case 4:
+ − 674 bridge.showCtrlDialog();
+ − 675 break;
+ − 676
+ − 677 case 5:
+ − 678 bridge.showCharPickerDialog();
+ − 679 }
+ − 680 }
+ − 681 });
+ − 682 AlertDialog alert = builder.create();
+ − 683 alert.show();
+ − 684 }
+ − 685 }
+ − 686 });
+ − 687 flip.setLongClickable(true);
+ − 688 flip.setOnTouchListener(new OnTouchListener() {
+ − 689 public boolean onTouch(View v, MotionEvent event) {
+ − 690 // when copying, highlight the area
+ − 691 if (copySource != null && copySource.isSelectingForCopy()) {
+ − 692 int row = (int)FloatMath.floor(event.getY() / copySource.charHeight);
+ − 693 int col = (int)FloatMath.floor(event.getX() / copySource.charWidth);
+ − 694 SelectionArea area = copySource.getSelectionArea();
+ − 695
+ − 696 switch (event.getAction()) {
+ − 697 case MotionEvent.ACTION_DOWN:
+ − 698
+ − 699 // recording starting area
+ − 700 if (area.isSelectingOrigin()) {
+ − 701 area.setRow(row);
+ − 702 area.setColumn(col);
+ − 703 lastTouchRow = row;
+ − 704 lastTouchCol = col;
+ − 705 copySource.redraw();
+ − 706 }
+ − 707
+ − 708 return true;
+ − 709
+ − 710 case MotionEvent.ACTION_MOVE:
+ − 711
+ − 712 /* ignore when user hasn't moved since last time so
+ − 713 * we can fine-tune with directional pad
+ − 714 */
+ − 715 if (row == lastTouchRow && col == lastTouchCol)
+ − 716 return true;
+ − 717
+ − 718 // if the user moves, start the selection for other corner
+ − 719 area.finishSelectingOrigin();
+ − 720 // update selected area
+ − 721 area.setRow(row);
+ − 722 area.setColumn(col);
+ − 723 lastTouchRow = row;
+ − 724 lastTouchCol = col;
+ − 725 copySource.redraw();
+ − 726 return true;
+ − 727
+ − 728 case MotionEvent.ACTION_UP:
+ − 729
+ − 730 /* If they didn't move their finger, maybe they meant to
+ − 731 * select the rest of the text with the directional pad.
+ − 732 */
+ − 733 if (area.getLeft() == area.getRight() &&
+ − 734 area.getTop() == area.getBottom()) {
+ − 735 return true;
+ − 736 }
+ − 737
+ − 738 // copy selected area to clipboard
+ − 739 String copiedText = area.copyFrom(copySource.buffer);
+ − 740 clipboard.setText(copiedText);
+ − 741 Toast.makeText(ConsoleActivity.this, getString(R.string.console_copy_done, copiedText.length()), Toast.LENGTH_LONG).show();
+ − 742
+ − 743 // fall through to clear state
+ − 744 case MotionEvent.ACTION_CANCEL:
+ − 745 // make sure we clear any highlighted area
+ − 746 area.reset();
+ − 747 copySource.setSelectingForCopy(false);
+ − 748 copySource.redraw();
+ − 749 return true;
+ − 750 }
+ − 751 }
+ − 752
+ − 753 Configuration config = getResources().getConfiguration();
+ − 754
+ − 755 if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ − 756 lastX = event.getX();
+ − 757 lastY = event.getY();
+ − 758 }
+ − 759 else if (event.getAction() == MotionEvent.ACTION_UP
+ − 760 && keyboardGroup.getVisibility() == View.GONE
+ − 761 && event.getEventTime() - event.getDownTime() < CLICK_TIME
+ − 762 && Math.abs(event.getX() - lastX) < MAX_CLICK_DISTANCE
+ − 763 && Math.abs(event.getY() - lastY) < MAX_CLICK_DISTANCE) {
+ − 764 showEmulatedKeys();
+ − 765 }
+ − 766
+ − 767 // pass any touch events back to detector
+ − 768 TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip);
+ − 769 return terminalView.mScaleDetector.onTouchEvent(event) | gestDetect.onTouchEvent(event);
+ − 770 }
+ − 771 });
+ − 772 }
+ − 773
+ − 774 /**
+ − 775 *
+ − 776 */
+ − 777 private void configureOrientation() {
+ − 778 String rotateDefault;
+ − 779
+ − 780 if (getResources().getConfiguration().keyboard == Configuration.KEYBOARD_NOKEYS)
+ − 781 rotateDefault = PreferenceConstants.ROTATION_PORTRAIT;
+ − 782 else
+ − 783 rotateDefault = PreferenceConstants.ROTATION_LANDSCAPE;
+ − 784
+ − 785 String rotate = prefs.getString(PreferenceConstants.ROTATION, rotateDefault);
+ − 786
+ − 787 if (PreferenceConstants.ROTATION_DEFAULT.equals(rotate))
+ − 788 rotate = rotateDefault;
+ − 789
+ − 790 // request a forced orientation if requested by user
+ − 791 if (PreferenceConstants.ROTATION_LANDSCAPE.equals(rotate)) {
+ − 792 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ − 793 forcedOrientation = true;
+ − 794 }
+ − 795 else if (PreferenceConstants.ROTATION_PORTRAIT.equals(rotate)) {
+ − 796 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ − 797 forcedOrientation = true;
+ − 798 }
+ − 799 else {
+ − 800 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
+ − 801 forcedOrientation = false;
+ − 802 }
+ − 803 }
+ − 804
+ − 805
+ − 806 @Override
+ − 807 public boolean onCreateOptionsMenu(Menu menu) {
+ − 808 super.onCreateOptionsMenu(menu);
+ − 809 View view = findCurrentView(R.id.console_flip);
+ − 810 final boolean activeTerminal = (view instanceof TerminalView);
+ − 811 boolean sessionOpen = false;
+ − 812 boolean disconnected = false;
+ − 813 boolean canForwardPorts = false;
+ − 814 boolean canTransferFiles = false;
+ − 815
+ − 816 if (activeTerminal) {
+ − 817 TerminalBridge bridge = ((TerminalView) view).bridge;
+ − 818 sessionOpen = bridge.isSessionOpen();
+ − 819 disconnected = bridge.isDisconnected();
+ − 820 canForwardPorts = bridge.canFowardPorts();
+ − 821 canTransferFiles = bridge.canTransferFiles();
+ − 822 }
+ − 823
+ − 824 menu.setQwertyMode(true);
+ − 825
+ − 826 if (!PreferenceConstants.PRE_HONEYCOMB) {
+ − 827 MenuItem ctrlKey = menu.add(getString(R.string.fullscreen));
+ − 828 ctrlKey.setEnabled(activeTerminal);
+ − 829 ctrlKey.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ − 830 ctrlKey.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ − 831 public boolean onMenuItemClick(MenuItem menuItem) {
+ − 832 if (fullScreen == FULLSCREEN_ON) {
+ − 833 setFullScreen(FULLSCREEN_OFF);
+ − 834 }
+ − 835 else
+ − 836 setFullScreen(FULLSCREEN_ON);
+ − 837
+ − 838 return true;
+ − 839 }
+ − 840 });
+ − 841 }
+ − 842
+ − 843 disconnect = menu.add(R.string.list_host_disconnect);
+ − 844
+ − 845 if (hardKeyboard)
+ − 846 disconnect.setAlphabeticShortcut('w');
+ − 847
+ − 848 if (!sessionOpen && disconnected)
+ − 849 disconnect.setTitle(R.string.console_menu_close);
+ − 850
+ − 851 disconnect.setEnabled(activeTerminal);
+ − 852 disconnect.setIcon(android.R.drawable.ic_menu_close_clear_cancel);
+ − 853 disconnect.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ − 854 public boolean onMenuItemClick(MenuItem item) {
+ − 855 // disconnect or close the currently visible session
+ − 856 TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip);
+ − 857 TerminalBridge bridge = terminalView.bridge;
+ − 858 bridge.dispatchDisconnect(true);
+ − 859 return true;
+ − 860 }
+ − 861 });
+ − 862 copy = menu.add(R.string.console_menu_copy);
+ − 863
+ − 864 if (hardKeyboard)
+ − 865 copy.setAlphabeticShortcut('c');
+ − 866
+ − 867 copy.setIcon(android.R.drawable.ic_menu_set_as);
+ − 868 copy.setEnabled(activeTerminal);
+ − 869 copy.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ − 870 public boolean onMenuItemClick(MenuItem item) {
+ − 871 // mark as copying and reset any previous bounds
+ − 872 TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip);
+ − 873 copySource = terminalView.bridge;
+ − 874 SelectionArea area = copySource.getSelectionArea();
+ − 875 area.reset();
+ − 876 area.setBounds(copySource.buffer.getColumns(), copySource.buffer.getRows());
+ − 877 copySource.setSelectingForCopy(true);
+ − 878 // Make sure we show the initial selection
+ − 879 copySource.redraw();
+ − 880 Toast.makeText(ConsoleActivity.this, getString(R.string.console_copy_start), Toast.LENGTH_LONG).show();
+ − 881 return true;
+ − 882 }
+ − 883 });
+ − 884 paste = menu.add(R.string.console_menu_paste);
+ − 885
+ − 886 if (hardKeyboard)
+ − 887 paste.setAlphabeticShortcut('v');
+ − 888
+ − 889 paste.setIcon(android.R.drawable.ic_menu_edit);
+ − 890 paste.setEnabled(clipboard.hasText() && sessionOpen);
+ − 891 paste.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ − 892 public boolean onMenuItemClick(MenuItem item) {
+ − 893 // force insert of clipboard text into current console
+ − 894 TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip);
+ − 895 TerminalBridge bridge = terminalView.bridge;
+ − 896 // pull string from clipboard and generate all events to force down
+ − 897 String clip = clipboard.getText().toString();
+ − 898 bridge.injectString(clip);
+ − 899 return true;
+ − 900 }
+ − 901 });
+ − 902 resize = menu.add(R.string.console_menu_resize);
+ − 903
+ − 904 if (hardKeyboard)
+ − 905 resize.setAlphabeticShortcut('r');
+ − 906
+ − 907 resize.setIcon(android.R.drawable.ic_menu_crop);
264
3e9eae36e5ef
menu force-size does not need a session, just an active terminal
Carl Byington <carl@five-ten-sg.com>
diff
changeset
+ − 908 resize.setEnabled(activeTerminal);
0
+ − 909 resize.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ − 910 public boolean onMenuItemClick(MenuItem item) {
+ − 911 final TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip);
+ − 912 final View resizeView = inflater.inflate(R.layout.dia_resize, null, false);
243
+ − 913 ((EditText) resizeView.findViewById(R.id.width)).setText("80");
+ − 914 ((EditText) resizeView.findViewById(R.id.height)).setText("25");
0
+ − 915 new AlertDialog.Builder(ConsoleActivity.this)
+ − 916 .setView(resizeView)
+ − 917 .setPositiveButton(R.string.button_resize, new DialogInterface.OnClickListener() {
+ − 918 public void onClick(DialogInterface dialog, int which) {
+ − 919 int width, height;
+ − 920
+ − 921 try {
+ − 922 width = Integer.parseInt(((EditText) resizeView
+ − 923 .findViewById(R.id.width))
+ − 924 .getText().toString());
+ − 925 height = Integer.parseInt(((EditText) resizeView
+ − 926 .findViewById(R.id.height))
+ − 927 .getText().toString());
+ − 928 }
+ − 929 catch (NumberFormatException nfe) {
+ − 930 // TODO change this to a real dialog where we can
+ − 931 // make the input boxes turn red to indicate an error.
+ − 932 return;
+ − 933 }
+ − 934
+ − 935 if (width > 0 && height > 0) {
+ − 936 terminalView.forceSize(width, height);
+ − 937 }
+ − 938 else {
+ − 939 new AlertDialog.Builder(ConsoleActivity.this)
+ − 940 .setTitle(R.string.resize_error_title)
+ − 941 .setMessage(R.string.resize_error_width_height)
+ − 942 .setNegativeButton(R.string.button_close, null)
+ − 943 .show();
+ − 944 }
+ − 945 }
+ − 946 }).setNeutralButton(R.string.button_resize_reset, new DialogInterface.OnClickListener() {
+ − 947 public void onClick(DialogInterface dialog, int which) {
+ − 948 terminalView.bridge.resetSize(terminalView);
+ − 949 }
+ − 950 }).setNegativeButton(android.R.string.cancel, null)
+ − 951 .create().show();
+ − 952 return true;
+ − 953 }
+ − 954 });
+ − 955 screenCapture = menu.add(R.string.console_menu_screencapture);
+ − 956
+ − 957 if (hardKeyboard)
+ − 958 screenCapture.setAlphabeticShortcut('s');
+ − 959
+ − 960 screenCapture.setIcon(android.R.drawable.ic_menu_camera);
+ − 961 screenCapture.setEnabled(activeTerminal);
+ − 962 screenCapture.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ − 963 public boolean onMenuItemClick(MenuItem item) {
+ − 964 final TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip);
+ − 965 terminalView.bridge.captureScreen();
+ − 966 return true;
+ − 967 }
+ − 968 });
+ − 969 portForward = menu.add(R.string.console_menu_portforwards);
+ − 970
+ − 971 if (hardKeyboard)
+ − 972 portForward.setAlphabeticShortcut('f');
+ − 973
+ − 974 portForward.setIcon(android.R.drawable.ic_menu_manage);
+ − 975 portForward.setEnabled(sessionOpen && canForwardPorts);
+ − 976 portForward.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ − 977 public boolean onMenuItemClick(MenuItem item) {
+ − 978 TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip);
+ − 979 TerminalBridge bridge = terminalView.bridge;
+ − 980 Intent intent = new Intent(ConsoleActivity.this, PortForwardListActivity.class);
+ − 981 intent.putExtra(Intent.EXTRA_TITLE, bridge.host.getId());
+ − 982 ConsoleActivity.this.startActivityForResult(intent, REQUEST_EDIT);
+ − 983 return true;
+ − 984 }
+ − 985 });
+ − 986 urlscan = menu.add(R.string.console_menu_urlscan);
+ − 987
+ − 988 if (hardKeyboard)
+ − 989 urlscan.setAlphabeticShortcut('l');
+ − 990
+ − 991 urlscan.setIcon(android.R.drawable.ic_menu_search);
+ − 992 urlscan.setEnabled(activeTerminal);
+ − 993 urlscan.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ − 994 public boolean onMenuItemClick(MenuItem item) {
+ − 995 View flip = findCurrentView(R.id.console_flip);
+ − 996
+ − 997 if (flip == null) return true;
+ − 998
+ − 999 TerminalView terminal = (TerminalView)flip;
+ − 1000 TerminalKeyListener handler = terminal.bridge.getKeyHandler();
+ − 1001 handler.urlScan(terminal);
+ − 1002 return true;
+ − 1003 }
+ − 1004 });
+ − 1005 download = menu.add(R.string.console_menu_download);
+ − 1006 download.setAlphabeticShortcut('d');
+ − 1007 download.setEnabled(sessionOpen && canTransferFiles);
+ − 1008 download.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ − 1009 public boolean onMenuItemClick(MenuItem item) {
+ − 1010 final String downloadFolder = prefs.getString(PreferenceConstants.DOWNLOAD_FOLDER, "");
+ − 1011 final TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip);
+ − 1012 final TerminalBridge bridge = terminalView.bridge;
+ − 1013 final EditText textField = new EditText(ConsoleActivity.this);
+ − 1014 new AlertDialog.Builder(ConsoleActivity.this)
+ − 1015 .setTitle(R.string.transfer_select_remote_download_title)
+ − 1016 .setMessage(R.string.transfer_select_remote_download_desc)
+ − 1017 .setView(textField)
+ − 1018 .setPositiveButton(R.string.transfer_button_download, new DialogInterface.OnClickListener() {
+ − 1019 public void onClick(DialogInterface dialog, int whichButton) {
+ − 1020 TransferThread transfer = new TransferThread(ConsoleActivity.this, handler);
+ − 1021
+ − 1022 if (!prefs.getBoolean(PreferenceConstants.BACKGROUND_FILE_TRANSFER, true))
+ − 1023 transfer.setProgressDialogMessage(getString(R.string.transfer_downloading));
+ − 1024
+ − 1025 transfer.download(bridge, textField.getText().toString(), null, downloadFolder);
+ − 1026 }
+ − 1027 }).setNegativeButton(android.R.string.cancel, null).create().show();
+ − 1028 return true;
+ − 1029 }
+ − 1030 });
+ − 1031 upload = menu.add(R.string.console_menu_upload);
+ − 1032 upload.setAlphabeticShortcut('u');
+ − 1033 upload.setEnabled(sessionOpen && canTransferFiles);
+ − 1034 upload.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ − 1035 public boolean onMenuItemClick(MenuItem item) {
+ − 1036 FileChooser.selectFile(ConsoleActivity.this, ConsoleActivity.this,
+ − 1037 FileChooser.REQUEST_CODE_SELECT_FILE,
+ − 1038 getString(R.string.file_chooser_select_file, getString(R.string.select_for_upload)));
+ − 1039 return true;
+ − 1040 }
+ − 1041 });
+ − 1042 return true;
+ − 1043 }
+ − 1044
+ − 1045 @Override
+ − 1046 protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ − 1047 super.onActivityResult(requestCode, resultCode, intent);
+ − 1048
+ − 1049 switch (requestCode) {
+ − 1050 case FileChooser.REQUEST_CODE_SELECT_FILE:
+ − 1051 if (resultCode == RESULT_OK && intent != null) {
+ − 1052 Uri uri = intent.getData();
+ − 1053
+ − 1054 try {
+ − 1055 if (uri != null) {
+ − 1056 fileSelected(new File(URI.create(uri.toString())));
+ − 1057 }
+ − 1058 else {
+ − 1059 String filename = intent.getDataString();
+ − 1060
+ − 1061 if (filename != null) {
+ − 1062 fileSelected(new File(URI.create(filename)));
+ − 1063 }
+ − 1064 }
+ − 1065 }
+ − 1066 catch (IllegalArgumentException e) {
+ − 1067 Log.e(TAG, "Couldn't read from selected file", e);
+ − 1068 }
+ − 1069 }
+ − 1070
+ − 1071 break;
+ − 1072 }
+ − 1073 }
+ − 1074
+ − 1075 public void fileSelected(final File f) {
+ − 1076 String destFileName;
+ − 1077 String uploadFolder = prefs.getString(PreferenceConstants.REMOTE_UPLOAD_FOLDER, null);
+ − 1078 final TransferThread transfer = new TransferThread(ConsoleActivity.this, handler);
+ − 1079 Log.d(TAG, "File chooser returned " + f);
+ − 1080
+ − 1081 if (uploadFolder == null)
+ − 1082 uploadFolder = "";
+ − 1083
+ − 1084 if (!uploadFolder.equals("") && uploadFolder.charAt(uploadFolder.length() - 1) != '/')
+ − 1085 destFileName = uploadFolder + "/" + f.getName();
+ − 1086 else
+ − 1087 destFileName = uploadFolder + f.getName();
+ − 1088
+ − 1089 if (prefs.getBoolean(PreferenceConstants.UPLOAD_DESTINATION_PROMPT, true)) {
+ − 1090 final EditText fileDest = new EditText(ConsoleActivity.this);
+ − 1091 fileDest.setSingleLine();
+ − 1092 fileDest.setText(destFileName);
+ − 1093 new AlertDialog.Builder(ConsoleActivity.this)
+ − 1094 .setTitle(R.string.transfer_select_remote_upload_dest_title)
+ − 1095 .setMessage(getResources().getString(R.string.transfer_select_remote_upload_dest_desc) + "\n" + f.getPath())
+ − 1096 .setView(fileDest)
+ − 1097 .setPositiveButton(R.string.transfer_button_upload, new DialogInterface.OnClickListener() {
+ − 1098 public void onClick(DialogInterface dialog, int whichButton) {
+ − 1099 if (!prefs.getBoolean(PreferenceConstants.BACKGROUND_FILE_TRANSFER, true))
+ − 1100 transfer.setProgressDialogMessage(getString(R.string.transfer_uploading));
+ − 1101
+ − 1102 TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip);
+ − 1103 TerminalBridge bridge = terminalView.bridge;
+ − 1104 File uf = new File(fileDest.getText().toString());
+ − 1105 String name = "", parent = "";
+ − 1106
+ − 1107 if (uf.getParent() != null)
+ − 1108 parent = uf.getParent().toString();
+ − 1109
+ − 1110 if (uf.getName() != null)
+ − 1111 name = uf.getName().toString();
+ − 1112
+ − 1113 transfer.upload(bridge, f.toString(), name, parent);
+ − 1114 }
+ − 1115 }).setNegativeButton(android.R.string.cancel, null).create().show();
+ − 1116 }
+ − 1117 else {
+ − 1118 if (!prefs.getBoolean(PreferenceConstants.BACKGROUND_FILE_TRANSFER, true))
+ − 1119 transfer.setProgressDialogMessage(getString(R.string.transfer_uploading));
+ − 1120
+ − 1121 TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip);
+ − 1122 TerminalBridge bridge = terminalView.bridge;
+ − 1123 transfer.upload(bridge, f.toString(), null, uploadFolder);
+ − 1124 }
+ − 1125 }
+ − 1126
+ − 1127 @Override
+ − 1128 public boolean onPrepareOptionsMenu(Menu menu) {
+ − 1129 super.onPrepareOptionsMenu(menu);
+ − 1130 setVolumeControlStream(AudioManager.STREAM_NOTIFICATION);
+ − 1131 final View view = findCurrentView(R.id.console_flip);
+ − 1132 boolean activeTerminal = (view instanceof TerminalView);
+ − 1133 boolean sessionOpen = false;
+ − 1134 boolean disconnected = false;
+ − 1135 boolean canForwardPorts = false;
+ − 1136 boolean canTransferFiles = false;
+ − 1137
+ − 1138 if (activeTerminal) {
+ − 1139 TerminalBridge bridge = ((TerminalView) view).bridge;
+ − 1140 sessionOpen = bridge.isSessionOpen();
+ − 1141 disconnected = bridge.isDisconnected();
+ − 1142 canForwardPorts = bridge.canFowardPorts();
+ − 1143 canTransferFiles = bridge.canTransferFiles();
+ − 1144 }
+ − 1145
+ − 1146 disconnect.setEnabled(activeTerminal);
+ − 1147
+ − 1148 if (sessionOpen || !disconnected)
+ − 1149 disconnect.setTitle(R.string.list_host_disconnect);
+ − 1150 else
+ − 1151 disconnect.setTitle(R.string.console_menu_close);
+ − 1152
+ − 1153 copy.setEnabled(activeTerminal);
+ − 1154 paste.setEnabled(clipboard.hasText() && sessionOpen);
+ − 1155 portForward.setEnabled(sessionOpen && canForwardPorts);
+ − 1156 urlscan.setEnabled(activeTerminal);
265
cf81ae79931d
menu force-size does not need a session, just an active terminal
Carl Byington <carl@five-ten-sg.com>
diff
changeset
+ − 1157 resize.setEnabled(activeTerminal);
0
+ − 1158 download.setEnabled(sessionOpen && canTransferFiles);
+ − 1159 upload.setEnabled(sessionOpen && canTransferFiles);
+ − 1160 return true;
+ − 1161 }
+ − 1162
+ − 1163 @Override
+ − 1164 public boolean onOptionsItemSelected(MenuItem item) {
+ − 1165 switch (item.getItemId()) {
+ − 1166 case android.R.id.home:
+ − 1167 Intent intent = new Intent(this, HostListActivity.class);
+ − 1168 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ − 1169 startActivity(intent);
+ − 1170 return true;
+ − 1171
+ − 1172 default:
+ − 1173 return super.onOptionsItemSelected(item);
+ − 1174 }
+ − 1175 }
+ − 1176
+ − 1177 @Override
+ − 1178 public void onOptionsMenuClosed(Menu menu) {
+ − 1179 super.onOptionsMenuClosed(menu);
+ − 1180 setVolumeControlStream(AudioManager.STREAM_MUSIC);
+ − 1181 }
+ − 1182
+ − 1183 @Override
+ − 1184 public void onStart() {
+ − 1185 super.onStart();
+ − 1186 // connect with manager service to find all bridges
+ − 1187 // when connected it will insert all views
+ − 1188 bindService(new Intent(this, TerminalManager.class), connection, Context.BIND_AUTO_CREATE);
+ − 1189
+ − 1190 if (getResources().getConfiguration().hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
+ − 1191 this.mKeyboardButton.setVisibility(View.GONE);
+ − 1192 }
+ − 1193 }
+ − 1194
+ − 1195 @Override
+ − 1196 public void onPause() {
+ − 1197 super.onPause();
+ − 1198 Log.d(TAG, "onPause called");
+ − 1199
+ − 1200 if (forcedOrientation && bound != null)
+ − 1201 bound.setResizeAllowed(false);
+ − 1202 }
+ − 1203
+ − 1204 @Override
+ − 1205 public void onResume() {
+ − 1206 super.onResume();
+ − 1207 Log.d(TAG, "onResume called");
+ − 1208
+ − 1209 // Make sure we don't let the screen fall asleep.
+ − 1210 // This also keeps the Wi-Fi chipset from disconnecting us.
+ − 1211 if (prefs.getBoolean(PreferenceConstants.KEEP_ALIVE, true)) {
+ − 1212 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ − 1213 }
+ − 1214 else {
+ − 1215 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ − 1216 }
+ − 1217
+ − 1218 configureOrientation();
+ − 1219
+ − 1220 if (forcedOrientation && bound != null)
+ − 1221 bound.setResizeAllowed(true);
+ − 1222 }
+ − 1223
+ − 1224 /* (non-Javadoc)
+ − 1225 * @see android.app.Activity#onNewIntent(android.content.Intent)
+ − 1226 */
+ − 1227 @Override
+ − 1228 protected void onNewIntent(Intent intent) {
+ − 1229 super.onNewIntent(intent);
+ − 1230 Log.d(TAG, "onNewIntent called");
+ − 1231 requested = intent.getData();
+ − 1232
+ − 1233 if (requested == null) {
+ − 1234 Log.e(TAG, "Got null intent data in onNewIntent()");
+ − 1235 return;
+ − 1236 }
+ − 1237
+ − 1238 if (bound == null) {
+ − 1239 Log.e(TAG, "We're not bound in onNewIntent()");
+ − 1240 return;
+ − 1241 }
+ − 1242
+ − 1243 TerminalBridge requestedBridge = bound.getConnectedBridge(requested.getFragment());
+ − 1244 int requestedIndex = 0;
+ − 1245
+ − 1246 synchronized (flip) {
+ − 1247 if (requestedBridge == null) {
+ − 1248 // If we didn't find the requested connection, try opening it
+ − 1249 try {
+ − 1250 Log.d(TAG, String.format("We couldnt find an existing bridge with URI=%s (nickname=%s)," +
+ − 1251 "so creating one now", requested.toString(), requested.getFragment()));
+ − 1252 requestedBridge = bound.openConnection(requested);
+ − 1253 }
+ − 1254 catch (Exception e) {
+ − 1255 Log.e(TAG, "Problem while trying to create new requested bridge from URI", e);
+ − 1256 // TODO: We should display an error dialog here.
+ − 1257 return;
+ − 1258 }
+ − 1259
+ − 1260 requestedIndex = addNewTerminalView(requestedBridge);
+ − 1261 }
+ − 1262 else {
+ − 1263 final int flipIndex = getFlipIndex(requestedBridge);
+ − 1264
+ − 1265 if (flipIndex > requestedIndex) {
+ − 1266 requestedIndex = flipIndex;
+ − 1267 }
+ − 1268 }
+ − 1269
+ − 1270 setDisplayedTerminal(requestedIndex);
+ − 1271 }
+ − 1272 }
+ − 1273
+ − 1274 @Override
+ − 1275 public void onStop() {
+ − 1276 super.onStop();
+ − 1277 unbindService(connection);
+ − 1278 }
+ − 1279
+ − 1280 protected void shiftCurrentTerminal(final int direction) {
+ − 1281 View overlay;
+ − 1282
+ − 1283 synchronized (flip) {
+ − 1284 boolean shouldAnimate = flip.getChildCount() > 1;
+ − 1285
+ − 1286 // Only show animation if there is something else to go to.
+ − 1287 if (shouldAnimate) {
+ − 1288 // keep current overlay from popping up again
+ − 1289 overlay = findCurrentView(R.id.terminal_overlay);
+ − 1290
+ − 1291 if (overlay != null)
+ − 1292 overlay.startAnimation(fade_stay_hidden);
+ − 1293
+ − 1294 if (direction == SHIFT_LEFT) {
+ − 1295 flip.setInAnimation(slide_left_in);
+ − 1296 flip.setOutAnimation(slide_left_out);
+ − 1297 flip.showNext();
+ − 1298 }
+ − 1299 else if (direction == SHIFT_RIGHT) {
+ − 1300 flip.setInAnimation(slide_right_in);
+ − 1301 flip.setOutAnimation(slide_right_out);
+ − 1302 flip.showPrevious();
+ − 1303 }
+ − 1304 }
+ − 1305
+ − 1306 updateDefault();
+ − 1307
+ − 1308 if (shouldAnimate) {
+ − 1309 // show overlay on new slide and start fade
+ − 1310 overlay = findCurrentView(R.id.terminal_overlay);
+ − 1311
+ − 1312 if (overlay != null)
+ − 1313 overlay.startAnimation(fade_out_delayed);
+ − 1314 }
+ − 1315
+ − 1316 updatePromptVisible();
+ − 1317 }
+ − 1318 }
+ − 1319
+ − 1320 /**
+ − 1321 * Save the currently shown {@link TerminalView} as the default. This is
+ − 1322 * saved back down into {@link TerminalManager} where we can read it again
+ − 1323 * later.
+ − 1324 */
+ − 1325 private void updateDefault() {
+ − 1326 // update the current default terminal
+ − 1327 View view = findCurrentView(R.id.console_flip);
+ − 1328
+ − 1329 if (!(view instanceof TerminalView)) return;
+ − 1330
+ − 1331 TerminalView terminal = (TerminalView)view;
+ − 1332
+ − 1333 if (bound != null) bound.defaultBridge = terminal.bridge;
+ − 1334
+ − 1335 // tell the bridge monitor it has the topmost visible window now.
+ − 1336 if (terminal.bridge.monitor != null) terminal.bridge.monitor.activate();
+ − 1337 }
+ − 1338
+ − 1339 protected void updateEmptyVisible() {
+ − 1340 // update visibility of empty status message
+ − 1341 empty.setVisibility((flip.getChildCount() == 0) ? View.VISIBLE : View.GONE);
+ − 1342 }
+ − 1343
+ − 1344 /**
+ − 1345 * Show any prompts requested by the currently visible {@link TerminalView}.
+ − 1346 */
+ − 1347 protected void updatePromptVisible() {
+ − 1348 // check if our currently-visible terminalbridge is requesting any prompt services
+ − 1349 View view = findCurrentView(R.id.console_flip);
+ − 1350 // Hide all the prompts in case a prompt request was canceled
+ − 1351 hideAllPrompts();
+ − 1352
+ − 1353 if (!(view instanceof TerminalView)) {
+ − 1354 // we dont have an active view, so hide any prompts
+ − 1355 return;
+ − 1356 }
+ − 1357
+ − 1358 PromptHelper prompt = ((TerminalView)view).bridge.promptHelper;
+ − 1359
+ − 1360 if (String.class.equals(prompt.promptRequested)) {
+ − 1361 stringPromptGroup.setVisibility(View.VISIBLE);
+ − 1362 String instructions = prompt.promptInstructions;
+ − 1363 boolean password = prompt.passwordRequested;
+ − 1364
+ − 1365 if (instructions != null && instructions.length() > 0) {
+ − 1366 stringPromptInstructions.setVisibility(View.VISIBLE);
+ − 1367 stringPromptInstructions.setText(instructions);
+ − 1368 }
+ − 1369 else
+ − 1370 stringPromptInstructions.setVisibility(View.GONE);
+ − 1371
+ − 1372 if (password) {
+ − 1373 stringPrompt.setInputType(InputType.TYPE_CLASS_TEXT |
+ − 1374 InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ − 1375 stringPrompt.setTransformationMethod(PasswordTransformationMethod.getInstance());
+ − 1376 }
+ − 1377 else {
+ − 1378 stringPrompt.setInputType(InputType.TYPE_CLASS_TEXT |
+ − 1379 InputType.TYPE_TEXT_FLAG_AUTO_CORRECT);
+ − 1380 stringPrompt.setTransformationMethod(SingleLineTransformationMethod.getInstance());
+ − 1381 }
+ − 1382
+ − 1383 stringPrompt.setText("");
+ − 1384 stringPrompt.setHint(prompt.promptHint);
+ − 1385 stringPrompt.requestFocus();
+ − 1386 }
+ − 1387 else if (Boolean.class.equals(prompt.promptRequested)) {
+ − 1388 booleanPromptGroup.setVisibility(View.VISIBLE);
+ − 1389 booleanPrompt.setText(prompt.promptHint);
+ − 1390 booleanYes.requestFocus();
+ − 1391 }
+ − 1392 else {
+ − 1393 hideAllPrompts();
+ − 1394 view.requestFocus();
+ − 1395 }
+ − 1396 }
+ − 1397
+ − 1398 @Override
+ − 1399 public void onConfigurationChanged(Configuration newConfig) {
+ − 1400 super.onConfigurationChanged(newConfig);
+ − 1401 Log.d(TAG, String.format("onConfigurationChanged; requestedOrientation=%d, newConfig.orientation=%d", getRequestedOrientation(), newConfig.orientation));
+ − 1402
+ − 1403 if (bound != null) {
+ − 1404 if (forcedOrientation &&
+ − 1405 (newConfig.orientation != Configuration.ORIENTATION_LANDSCAPE &&
+ − 1406 getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) ||
+ − 1407 (newConfig.orientation != Configuration.ORIENTATION_PORTRAIT &&
+ − 1408 getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT))
+ − 1409 bound.setResizeAllowed(false);
+ − 1410 else
+ − 1411 bound.setResizeAllowed(true);
+ − 1412
+ − 1413 bound.hardKeyboardHidden = (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES);
+ − 1414 mKeyboardButton.setVisibility(bound.hardKeyboardHidden ? View.VISIBLE : View.GONE);
+ − 1415 }
+ − 1416 }
+ − 1417
+ − 1418 /**
+ − 1419 * Adds a new TerminalBridge to the current set of views in our ViewFlipper.
+ − 1420 *
+ − 1421 * @param bridge TerminalBridge to add to our ViewFlipper
+ − 1422 * @return the child index of the new view in the ViewFlipper
+ − 1423 */
+ − 1424 private int addNewTerminalView(TerminalBridge bridge) {
+ − 1425 // let them know about our prompt handler services
+ − 1426 bridge.promptHelper.setHandler(promptHandler);
+ − 1427 // inflate each terminal view
+ − 1428 RelativeLayout view = (RelativeLayout)inflater.inflate(R.layout.item_terminal, flip, false);
+ − 1429 // set the terminal overlay text
+ − 1430 TextView overlay = (TextView)view.findViewById(R.id.terminal_overlay);
+ − 1431 overlay.setText(bridge.host.getNickname());
+ − 1432 // and add our terminal view control, using index to place behind overlay
+ − 1433 TerminalView terminal = new TerminalView(ConsoleActivity.this, bridge);
+ − 1434 terminal.setId(R.id.console_flip);
+ − 1435 view.addView(terminal, 0);
+ − 1436
+ − 1437 synchronized (flip) {
+ − 1438 // finally attach to the flipper
+ − 1439 flip.addView(view);
+ − 1440 return flip.getChildCount() - 1;
+ − 1441 }
+ − 1442 }
+ − 1443
+ − 1444 private int getFlipIndex(TerminalBridge bridge) {
+ − 1445 synchronized (flip) {
+ − 1446 final int children = flip.getChildCount();
+ − 1447
+ − 1448 for (int i = 0; i < children; i++) {
+ − 1449 final View view = flip.getChildAt(i).findViewById(R.id.console_flip);
+ − 1450
+ − 1451 if (view == null || !(view instanceof TerminalView)) {
+ − 1452 // How did that happen?
+ − 1453 continue;
+ − 1454 }
+ − 1455
+ − 1456 final TerminalView tv = (TerminalView) view;
+ − 1457
+ − 1458 if (tv.bridge == bridge) {
+ − 1459 return i;
+ − 1460 }
+ − 1461 }
+ − 1462 }
+ − 1463
+ − 1464 return -1;
+ − 1465 }
+ − 1466
+ − 1467 /**
+ − 1468 * Displays the child in the ViewFlipper at the requestedIndex and updates the prompts.
+ − 1469 *
+ − 1470 * @param requestedIndex the index of the terminal view to display
+ − 1471 */
+ − 1472 private void setDisplayedTerminal(int requestedIndex) {
+ − 1473 synchronized (flip) {
+ − 1474 try {
+ − 1475 // show the requested bridge if found, also fade out overlay
+ − 1476 flip.setDisplayedChild(requestedIndex);
+ − 1477 flip.getCurrentView().findViewById(R.id.terminal_overlay)
+ − 1478 .startAnimation(fade_out_delayed);
+ − 1479 }
+ − 1480 catch (NullPointerException npe) {
+ − 1481 Log.d(TAG, "View went away when we were about to display it", npe);
+ − 1482 }
+ − 1483
+ − 1484 updatePromptVisible();
+ − 1485 updateEmptyVisible();
+ − 1486 }
+ − 1487 }
+ − 1488
+ − 1489 private void setFullScreen(int fullScreen) {
+ − 1490 if (fullScreen != this.fullScreen) {
+ − 1491 if (fullScreen == FULLSCREEN_ON) {
+ − 1492 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ − 1493 WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ − 1494 }
+ − 1495 else {
+ − 1496 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ − 1497 }
+ − 1498
+ − 1499 this.fullScreen = fullScreen;
+ − 1500
+ − 1501 if (bound != null)
+ − 1502 bound.setFullScreen(this.fullScreen);
+ − 1503 }
+ − 1504 }
+ − 1505 }