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