Mercurial > 510Connectbot
annotate src/com/five_ten_sg/connectbot/ConsoleActivity.java @ 405:d3c5480e8441
fix broken fillRegenerationBuffer/fillScreenArray attempt to handle missing attributes; fix setfield outside screen boundaries
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Fri, 24 Oct 2014 14:19:06 -0700 |
parents | cf81ae79931d |
children | 885f2bca6032 |
rev | line source |
---|---|
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>
parents:
243
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>
parents:
263
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
918dfcb862ed
add host preference for fixed screen size
Carl Byington <carl@five-ten-sg.com>
parents:
242
diff
changeset
|
913 ((EditText) resizeView.findViewById(R.id.width)).setText("80"); |
918dfcb862ed
add host preference for fixed screen size
Carl Byington <carl@five-ten-sg.com>
parents:
242
diff
changeset
|
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>
parents:
264
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 } |