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