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