Android-x86
Fork
Spenden

  • R/O
  • HTTP
  • SSH
  • HTTPS

packages-apps-ConnectBot: Commit

packages/apps/ConnectBot


Commit MetaInfo

Revision62f7ef4e11d2d7d814c89f72ec48b9acd83ab671 (tree)
Zeit2010-03-16 10:53:58
AutorKenny Root <kenny@the-...>
CommiterChih-Wei Huang

Log Message

Update to r493

Fix inverted logic for in-memory key use

Ändern Zusammenfassung

Diff

--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2,7 +2,7 @@
22 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
33 package="org.connectbot"
44 android:versionName="1.7-dev"
5- android:versionCode="259">
5+ android:versionCode="263">
66
77 <application
88 android:icon="@drawable/icon"
--- a/src/org/connectbot/ConsoleActivity.java
+++ b/src/org/connectbot/ConsoleActivity.java
@@ -90,6 +90,10 @@ public class ConsoleActivity extends Activity {
9090 private static final float MAX_CLICK_DISTANCE = 25f;
9191 private static final int KEYBOARD_DISPLAY_TIME = 1250;
9292
93+ // Direction to shift the ViewFlipper
94+ private static final int SHIFT_LEFT = 0;
95+ private static final int SHIFT_RIGHT = 1;
96+
9397 protected ViewFlipper flip = null;
9498 protected TerminalManager bound = null;
9599 protected LayoutInflater inflater = null;
@@ -141,27 +145,17 @@ public class ConsoleActivity extends Activity {
141145
142146 // clear out any existing bridges and record requested index
143147 flip.removeAllViews();
144- String requestedNickname = (requested != null) ? requested.getFragment() : null;
145- int requestedIndex = 0;
146148
147- // first check if we need to create a new session for requested
148- boolean found = false;
149- for (TerminalBridge bridge : bound.bridges) {
150- String nick = bridge.host.getNickname();
151- if (nick == null)
152- continue;
149+ final String requestedNickname = (requested != null) ? requested.getFragment() : null;
150+ int requestedIndex = 0;
153151
154- if (nick.equals(requestedNickname)) {
155- found = true;
156- break;
157- }
158- }
152+ TerminalBridge requestedBridge = bound.getConnectedBridge(requestedNickname);
159153
160154 // If we didn't find the requested connection, try opening it
161- if (!found) {
155+ if (requestedNickname != null && requestedBridge == null) {
162156 try {
163157 Log.d(TAG, String.format("We couldnt find an existing bridge with URI=%s (nickname=%s), so creating one now", requested.toString(), requestedNickname));
164- bound.openConnection(requested);
158+ requestedBridge = bound.openConnection(requested);
165159 } catch(Exception e) {
166160 Log.e(TAG, "Problem while trying to create new requested bridge from URI", e);
167161 }
@@ -170,46 +164,22 @@ public class ConsoleActivity extends Activity {
170164 // create views for all bridges on this service
171165 for (TerminalBridge bridge : bound.bridges) {
172166
173- // let them know about our prompt handler services
174- bridge.promptHelper.setHandler(promptHandler);
175- bridge.refreshKeymode();
176-
177- // inflate each terminal view
178- RelativeLayout view = (RelativeLayout)inflater.inflate(R.layout.item_terminal, flip, false);
179-
180- // set the terminal overlay text
181- TextView overlay = (TextView)view.findViewById(R.id.terminal_overlay);
182- overlay.setText(bridge.host.getNickname());
183-
184- // and add our terminal view control, using index to place behind overlay
185- TerminalView terminal = new TerminalView(ConsoleActivity.this, bridge);
186- terminal.setId(R.id.console_flip);
187- view.addView(terminal, 0);
188-
189- // finally attach to the flipper
190- flip.addView(view);
167+ final int currentIndex = addNewTerminalView(bridge);
191168
192169 // check to see if this bridge was requested
193- if (bridge.host.getNickname().equals(requestedNickname))
194- requestedIndex = flip.getChildCount() - 1;
170+ if (bridge == requestedBridge)
171+ requestedIndex = currentIndex;
195172 }
196173
197- try {
198- // show the requested bridge if found, also fade out overlay
199- flip.setDisplayedChild(requestedIndex);
200- flip.getCurrentView().findViewById(R.id.terminal_overlay).startAnimation(fade_out_delayed);
201- } catch (NullPointerException npe) {
202- Log.d(TAG, "View went away when we were about to display it", npe);
203- }
204-
205- updatePromptVisible();
206- updateEmptyVisible();
174+ setDisplayedTerminal(requestedIndex);
207175 }
208176
209177 public void onServiceDisconnected(ComponentName className) {
210178 // tell each bridge to forget about our prompt handler
211- for(TerminalBridge bridge : bound.bridges)
212- bridge.promptHelper.setHandler(null);
179+ synchronized (bound.bridges) {
180+ for(TerminalBridge bridge : bound.bridges)
181+ bridge.promptHelper.setHandler(null);
182+ }
213183
214184 flip.removeAllViews();
215185 updateEmptyVisible();
@@ -242,37 +212,32 @@ public class ConsoleActivity extends Activity {
242212 /**
243213 * @param bridge
244214 */
245- private void closeBridge(TerminalBridge bridge) {
246- for(int i = 0; i < flip.getChildCount(); i++) {
247- View child = flip.getChildAt(i).findViewById(R.id.console_flip);
248-
249- if (!(child instanceof TerminalView)) continue;
215+ private void closeBridge(final TerminalBridge bridge) {
216+ synchronized (flip) {
217+ final int flipIndex = getFlipIndex(bridge);
250218
251- TerminalView terminal = (TerminalView) child;
252-
253- if (terminal.bridge.equals(bridge)) {
254- // we've found the terminal to remove
255- // shift something into its place if currently visible
256- if(flip.getDisplayedChild() == i)
257- shiftLeft();
258- flip.removeViewAt(i);
219+ if (flipIndex >= 0) {
220+ if (flip.getDisplayedChild() == flipIndex) {
221+ shiftCurrentTerminal(SHIFT_LEFT);
222+ }
223+ flip.removeViewAt(flipIndex);
259224
260225 /* TODO Remove this workaround when ViewFlipper is fixed to listen
261226 * to view removals. Android Issue 1784
262227 */
263228 final int numChildren = flip.getChildCount();
264229 if (flip.getDisplayedChild() >= numChildren &&
265- numChildren > 0)
230+ numChildren > 0) {
266231 flip.setDisplayedChild(numChildren - 1);
232+ }
267233
268234 updateEmptyVisible();
269- break;
270235 }
271- }
272236
273- // If we just closed the last bridge, go back to the previous activity.
274- if (flip.getChildCount() == 0) {
275- finish();
237+ // If we just closed the last bridge, go back to the previous activity.
238+ if (flip.getChildCount() == 0) {
239+ finish();
240+ }
276241 }
277242 }
278243
@@ -429,20 +394,20 @@ public class ConsoleActivity extends Activity {
429394 @Override
430395 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
431396
432- float distx = e2.getRawX() - e1.getRawX();
433- float disty = e2.getRawY() - e1.getRawY();
434- int goalwidth = flip.getWidth() / 2;
397+ final float distx = e2.getRawX() - e1.getRawX();
398+ final float disty = e2.getRawY() - e1.getRawY();
399+ final int goalwidth = flip.getWidth() / 2;
435400
436401 // need to slide across half of display to trigger console change
437402 // make sure user kept a steady hand horizontally
438- if(Math.abs(disty) < 100) {
439- if(distx > goalwidth) {
440- shiftRight();
403+ if (Math.abs(disty) < (flip.getHeight() / 4)) {
404+ if (distx > goalwidth) {
405+ shiftCurrentTerminal(SHIFT_RIGHT);
441406 return true;
442407 }
443408
444- if(distx < -goalwidth) {
445- shiftLeft();
409+ if (distx < -goalwidth) {
410+ shiftCurrentTerminal(SHIFT_LEFT);
446411 return true;
447412 }
448413
@@ -463,12 +428,12 @@ public class ConsoleActivity extends Activity {
463428 return false;
464429
465430 // if releasing then reset total scroll
466- if(e2.getAction() == MotionEvent.ACTION_UP) {
431+ if (e2.getAction() == MotionEvent.ACTION_UP) {
467432 totalY = 0;
468433 }
469434
470435 // activate consider if within x tolerance
471- if(Math.abs(e1.getX() - e2.getX()) < ViewConfiguration.getTouchSlop() * 4) {
436+ if (Math.abs(e1.getX() - e2.getX()) < ViewConfiguration.getTouchSlop() * 4) {
472437
473438 View flip = findCurrentView(R.id.console_flip);
474439 if(flip == null) return false;
@@ -477,11 +442,11 @@ public class ConsoleActivity extends Activity {
477442 // estimate how many rows we have scrolled through
478443 // accumulate distance that doesn't trigger immediate scroll
479444 totalY += distanceY;
480- int moved = (int)(totalY / terminal.bridge.charHeight);
445+ final int moved = (int)(totalY / terminal.bridge.charHeight);
481446
482447 // consume as scrollback only if towards right half of screen
483- if (e2.getX() > flip.getWidth() / 2.0) {
484- if(moved != 0) {
448+ if (e2.getX() > flip.getWidth() / 2) {
449+ if (moved != 0) {
485450 int base = terminal.bridge.buffer.getWindowBase();
486451 terminal.bridge.buffer.setWindowBase(base + moved);
487452 totalY = 0;
@@ -489,12 +454,12 @@ public class ConsoleActivity extends Activity {
489454 }
490455 } else {
491456 // otherwise consume as pgup/pgdown for every 5 lines
492- if(moved > 5) {
457+ if (moved > 5) {
493458 ((vt320)terminal.bridge.buffer).keyPressed(vt320.KEY_PAGE_DOWN, ' ', 0);
494459 terminal.bridge.tryKeyVibrate();
495460 totalY = 0;
496461 return true;
497- } else if(moved < -5) {
462+ } else if (moved < -5) {
498463 ((vt320)terminal.bridge.buffer).keyPressed(vt320.KEY_PAGE_UP, ' ', 0);
499464 terminal.bridge.tryKeyVibrate();
500465 totalY = 0;
@@ -868,6 +833,54 @@ public class ConsoleActivity extends Activity {
868833 bound.setResizeAllowed(true);
869834 }
870835
836+ /* (non-Javadoc)
837+ * @see android.app.Activity#onNewIntent(android.content.Intent)
838+ */
839+ @Override
840+ protected void onNewIntent(Intent intent) {
841+ super.onNewIntent(intent);
842+
843+ Log.d(TAG, "onNewIntent called");
844+
845+ requested = intent.getData();
846+
847+ if (requested == null) {
848+ Log.e(TAG, "Got null intent data in onNewIntent()");
849+ return;
850+ }
851+
852+ if (bound == null) {
853+ Log.e(TAG, "We're not bound in onNewIntent()");
854+ return;
855+ }
856+
857+ TerminalBridge requestedBridge = bound.getConnectedBridge(requested.getFragment());
858+ int requestedIndex = 0;
859+
860+ synchronized (flip) {
861+ if (requestedBridge == null) {
862+ // If we didn't find the requested connection, try opening it
863+
864+ try {
865+ Log.d(TAG, String.format("We couldnt find an existing bridge with URI=%s (nickname=%s),"+
866+ "so creating one now", requested.toString(), requested.getFragment()));
867+ requestedBridge = bound.openConnection(requested);
868+ } catch(Exception e) {
869+ Log.e(TAG, "Problem while trying to create new requested bridge from URI", e);
870+ }
871+
872+ requestedIndex = addNewTerminalView(requestedBridge);
873+ } else {
874+ final int flipIndex = getFlipIndex(requestedBridge);
875+ if (flipIndex > requestedIndex) {
876+ requestedIndex = flipIndex;
877+ }
878+ }
879+
880+ setDisplayedTerminal(requestedIndex);
881+ }
882+ }
883+
871884 @Override
872885 public void onStop() {
873886 super.onStop();
@@ -879,60 +892,40 @@ public class ConsoleActivity extends Activity {
879892 wakelock.release();
880893 }
881894
882- protected void shiftLeft() {
895+ protected void shiftCurrentTerminal(final int direction) {
883896 View overlay;
884- boolean shouldAnimate = flip.getChildCount() > 1;
885-
886- // Only show animation if there is something else to go to.
887- if (shouldAnimate) {
888- // keep current overlay from popping up again
889- overlay = findCurrentView(R.id.terminal_overlay);
890- if (overlay != null)
891- overlay.startAnimation(fade_stay_hidden);
892-
893- flip.setInAnimation(slide_left_in);
894- flip.setOutAnimation(slide_left_out);
895- flip.showNext();
896- }
897-
898- ConsoleActivity.this.updateDefault();
899-
900- if (shouldAnimate) {
901- // show overlay on new slide and start fade
902- overlay = findCurrentView(R.id.terminal_overlay);
903- if (overlay != null)
904- overlay.startAnimation(fade_out_delayed);
905- }
906-
907- updatePromptVisible();
908- }
897+ synchronized (flip) {
898+ boolean shouldAnimate = flip.getChildCount() > 1;
899+
900+ // Only show animation if there is something else to go to.
901+ if (shouldAnimate) {
902+ // keep current overlay from popping up again
903+ overlay = findCurrentView(R.id.terminal_overlay);
904+ if (overlay != null)
905+ overlay.startAnimation(fade_stay_hidden);
906+
907+ if (direction == SHIFT_LEFT) {
908+ flip.setInAnimation(slide_left_in);
909+ flip.setOutAnimation(slide_left_out);
910+ flip.showNext();
911+ } else if (direction == SHIFT_RIGHT) {
912+ flip.setInAnimation(slide_right_in);
913+ flip.setOutAnimation(slide_right_out);
914+ flip.showPrevious();
915+ }
916+ }
909917
910- protected void shiftRight() {
911- View overlay;
912- boolean shouldAnimate = flip.getChildCount() > 1;
913-
914- // Only show animation if there is something else to go to.
915- if (shouldAnimate) {
916- // keep current overlay from popping up again
917- overlay = findCurrentView(R.id.terminal_overlay);
918- if (overlay != null)
919- overlay.startAnimation(fade_stay_hidden);
920-
921- flip.setInAnimation(slide_right_in);
922- flip.setOutAnimation(slide_right_out);
923- flip.showPrevious();
924- }
918+ ConsoleActivity.this.updateDefault();
925919
926- ConsoleActivity.this.updateDefault();
920+ if (shouldAnimate) {
921+ // show overlay on new slide and start fade
922+ overlay = findCurrentView(R.id.terminal_overlay);
923+ if (overlay != null)
924+ overlay.startAnimation(fade_out_delayed);
925+ }
927926
928- if (shouldAnimate) {
929- // show overlay on new slide and start fade
930- overlay = findCurrentView(R.id.terminal_overlay);
931- if (overlay != null)
932- overlay.startAnimation(fade_out_delayed);
927+ updatePromptVisible();
933928 }
934-
935- updatePromptVisible();
936929 }
937930
938931 /**
@@ -1039,6 +1032,80 @@ public class ConsoleActivity extends Activity {
10391032 bound.setResizeAllowed(false);
10401033 else
10411034 bound.setResizeAllowed(true);
1035+
1036+ bound.hardKeyboardHidden = (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES);
1037+ }
1038+ }
1039+
1040+ /**
1041+ * Adds a new TerminalBridge to the current set of views in our ViewFlipper.
1042+ *
1043+ * @param bridge TerminalBridge to add to our ViewFlipper
1044+ * @return the child index of the new view in the ViewFlipper
1045+ */
1046+ private int addNewTerminalView(TerminalBridge bridge) {
1047+ // let them know about our prompt handler services
1048+ bridge.promptHelper.setHandler(promptHandler);
1049+
1050+ // inflate each terminal view
1051+ RelativeLayout view = (RelativeLayout)inflater.inflate(R.layout.item_terminal, flip, false);
1052+
1053+ // set the terminal overlay text
1054+ TextView overlay = (TextView)view.findViewById(R.id.terminal_overlay);
1055+ overlay.setText(bridge.host.getNickname());
1056+
1057+ // and add our terminal view control, using index to place behind overlay
1058+ TerminalView terminal = new TerminalView(ConsoleActivity.this, bridge);
1059+ terminal.setId(R.id.console_flip);
1060+ view.addView(terminal, 0);
1061+
1062+ synchronized (flip) {
1063+ // finally attach to the flipper
1064+ flip.addView(view);
1065+ return flip.getChildCount() - 1;
1066+ }
1067+ }
1068+
1069+ private int getFlipIndex(TerminalBridge bridge) {
1070+ synchronized (flip) {
1071+ final int children = flip.getChildCount();
1072+ for (int i = 0; i < children; i++) {
1073+ final View view = flip.getChildAt(i).findViewById(R.id.console_flip);
1074+
1075+ if (view == null || !(view instanceof TerminalView)) {
1076+ // How did that happen?
1077+ continue;
1078+ }
1079+
1080+ final TerminalView tv = (TerminalView) view;
1081+
1082+ if (tv.bridge == bridge) {
1083+ return i;
1084+ }
1085+ }
1086+ }
1087+
1088+ return -1;
1089+ }
1090+
1091+ /**
1092+ * Displays the child in the ViewFlipper at the requestedIndex and updates the prompts.
1093+ *
1094+ * @param requestedIndex the index of the terminal view to display
1095+ */
1096+ private void setDisplayedTerminal(int requestedIndex) {
1097+ synchronized (flip) {
1098+ try {
1099+ // show the requested bridge if found, also fade out overlay
1100+ flip.setDisplayedChild(requestedIndex);
1101+ flip.getCurrentView().findViewById(R.id.terminal_overlay)
1102+ .startAnimation(fade_out_delayed);
1103+ } catch (NullPointerException npe) {
1104+ Log.d(TAG, "View went away when we were about to display it", npe);
1105+ }
1106+
1107+ updatePromptVisible();
1108+ updateEmptyVisible();
10421109 }
10431110 }
10441111 }
--- a/src/org/connectbot/HostEditorActivity.java
+++ b/src/org/connectbot/HostEditorActivity.java
@@ -240,13 +240,7 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr
240240 public void onServiceConnected(ComponentName className, IBinder service) {
241241 TerminalManager bound = ((TerminalManager.TerminalBinder) service).getService();
242242
243- for (TerminalBridge bridge: bound.bridges) {
244- if (bridge.host.equals(host)) {
245- hostBridge = bridge;
246- Log.d(TAG, "Found host bridge; charset updates will be made live");
247- break;
248- }
249- }
243+ hostBridge = bound.getConnectedBridge(host);
250244 }
251245
252246 public void onServiceDisconnected(ComponentName name) {
--- a/src/org/connectbot/HostListActivity.java
+++ b/src/org/connectbot/HostListActivity.java
@@ -330,7 +330,7 @@ public class HostListActivity extends ListActivity {
330330
331331 // edit, disconnect, delete
332332 MenuItem connect = menu.add(R.string.list_host_disconnect);
333- final TerminalBridge bridge = bound.findBridge(host);
333+ final TerminalBridge bridge = bound.getConnectedBridge(host);
334334 connect.setEnabled((bridge != null));
335335 connect.setOnMenuItemClickListener(new OnMenuItemClickListener() {
336336 public boolean onMenuItemClick(MenuItem item) {
@@ -473,7 +473,7 @@ public class HostListActivity extends ListActivity {
473473 if (this.manager == null)
474474 return STATE_UNKNOWN;
475475
476- if (manager.findBridge(host) != null)
476+ if (manager.getConnectedBridge(host) != null)
477477 return STATE_CONNECTED;
478478
479479 if (manager.disconnected.contains(host))
--- a/src/org/connectbot/PortForwardListActivity.java
+++ b/src/org/connectbot/PortForwardListActivity.java
@@ -132,16 +132,8 @@ public class PortForwardListActivity extends ListActivity {
132132 public void onServiceConnected(ComponentName className, IBinder service) {
133133 TerminalManager bound = ((TerminalManager.TerminalBinder) service).getService();
134134
135- for (TerminalBridge bridge: bound.bridges) {
136- if (bridge.host.equals(host)) {
137- hostBridge = bridge;
138- updateHandler.sendEmptyMessage(-1);
139- Log.d(TAG, "Found host bridge; using that instead of database");
140- break;
141- }
142- }
143-
144-
135+ hostBridge = bound.getConnectedBridge(host);
136+ updateHandler.sendEmptyMessage(-1);
145137 }
146138
147139 public void onServiceDisconnected(ComponentName name) {
--- a/src/org/connectbot/PubkeyListActivity.java
+++ b/src/org/connectbot/PubkeyListActivity.java
@@ -292,10 +292,8 @@ public class PubkeyListActivity extends ListActivity implements EventListener {
292292
293293 Log.d(TAG, String.format("Unlocked key '%s'", pubkey.getNickname()));
294294
295- // save this key in-memory if option enabled
296- if(bound.isSavingKeys()) {
297- bound.addKey(pubkey, trileadKey);
298- }
295+ // save this key in memory
296+ bound.addKey(pubkey, trileadKey);
299297
300298 updateHandler.sendEmptyMessage(-1);
301299 }
--- a/src/org/connectbot/TerminalView.java
+++ b/src/org/connectbot/TerminalView.java
@@ -20,6 +20,7 @@ package org.connectbot;
2020 import org.connectbot.bean.SelectionArea;
2121 import org.connectbot.service.FontSizeChangedListener;
2222 import org.connectbot.service.TerminalBridge;
23+import org.connectbot.service.TerminalKeyListener;
2324
2425 import android.app.Activity;
2526 import android.content.Context;
@@ -110,7 +111,7 @@ public class TerminalView extends View implements FontSizeChangedListener {
110111 bridge.addFontSizeChangedListener(this);
111112
112113 // connect our view up to the bridge
113- setOnKeyListener(bridge);
114+ setOnKeyListener(bridge.getKeyHandler());
114115 }
115116
116117 public void destroy() {
@@ -149,14 +150,18 @@ public class TerminalView extends View implements FontSizeChangedListener {
149150 // also draw cursor if visible
150151 if (bridge.buffer.isCursorVisible()) {
151152 int cursorColumn = bridge.buffer.getCursorColumn();
152- int columns = bridge.buffer.getColumns();
153+ final int cursorRow = bridge.buffer.getCursorRow();
154+
155+ final int columns = bridge.buffer.getColumns();
153156
154157 if (cursorColumn == columns)
155158 cursorColumn = columns - 1;
156159
160+ if (cursorColumn < 0 || cursorRow < 0)
161+ return;
162+
157163 int currentAttribute = bridge.buffer.getAttributes(
158- cursorColumn,
159- bridge.buffer.getCursorRow());
164+ cursorColumn, cursorRow);
160165 boolean onWideCharacter = (currentAttribute & VDUBuffer.FULLWIDTH) != 0;
161166
162167 int x = cursorColumn * bridge.charWidth;
@@ -176,21 +181,21 @@ public class TerminalView extends View implements FontSizeChangedListener {
176181 // Make sure we scale our decorations to the correct size.
177182 canvas.concat(scaleMatrix);
178183
179- int metaState = bridge.getMetaState();
184+ int metaState = bridge.getKeyHandler().getMetaState();
180185
181- if ((metaState & TerminalBridge.META_SHIFT_ON) != 0)
186+ if ((metaState & TerminalKeyListener.META_SHIFT_ON) != 0)
182187 canvas.drawPath(shiftCursor, cursorStrokePaint);
183- else if ((metaState & TerminalBridge.META_SHIFT_LOCK) != 0)
188+ else if ((metaState & TerminalKeyListener.META_SHIFT_LOCK) != 0)
184189 canvas.drawPath(shiftCursor, cursorPaint);
185190
186- if ((metaState & TerminalBridge.META_ALT_ON) != 0)
191+ if ((metaState & TerminalKeyListener.META_ALT_ON) != 0)
187192 canvas.drawPath(altCursor, cursorStrokePaint);
188- else if ((metaState & TerminalBridge.META_ALT_LOCK) != 0)
193+ else if ((metaState & TerminalKeyListener.META_ALT_LOCK) != 0)
189194 canvas.drawPath(altCursor, cursorPaint);
190195
191- if ((metaState & TerminalBridge.META_CTRL_ON) != 0)
196+ if ((metaState & TerminalKeyListener.META_CTRL_ON) != 0)
192197 canvas.drawPath(ctrlCursor, cursorStrokePaint);
193- else if ((metaState & TerminalBridge.META_CTRL_LOCK) != 0)
198+ else if ((metaState & TerminalKeyListener.META_CTRL_LOCK) != 0)
194199 canvas.drawPath(ctrlCursor, cursorPaint);
195200
196201 // Restore previous clip region
--- /dev/null
+++ b/src/org/connectbot/service/ConnectivityReceiver.java
@@ -0,0 +1,155 @@
1+/**
2+ *
3+ */
4+package org.connectbot.service;
5+
6+import android.content.BroadcastReceiver;
7+import android.content.Context;
8+import android.content.Intent;
9+import android.content.IntentFilter;
10+import android.net.ConnectivityManager;
11+import android.net.NetworkInfo;
12+import android.net.NetworkInfo.State;
13+import android.net.wifi.WifiManager;
14+import android.net.wifi.WifiManager.WifiLock;
15+import android.util.Log;
16+
17+/**
18+ * @author kroot
19+ *
20+ */
21+public class ConnectivityReceiver extends BroadcastReceiver {
22+ private static final String TAG = "ConnectBot.ConnectivityManager";
23+
24+ private boolean mIsConnected = false;
25+
26+ final private TerminalManager mTerminalManager;
27+
28+ final private WifiLock mWifiLock;
29+
30+ private int mNetworkRef = 0;
31+
32+ private boolean mLockingWifi;
33+
34+ public ConnectivityReceiver(TerminalManager manager, boolean lockingWifi) {
35+ mTerminalManager = manager;
36+
37+ final ConnectivityManager cm =
38+ (ConnectivityManager) manager.getSystemService(Context.CONNECTIVITY_SERVICE);
39+
40+ final WifiManager wm = (WifiManager) manager.getSystemService(Context.WIFI_SERVICE);
41+ mWifiLock = wm.createWifiLock(TAG);
42+
43+ final NetworkInfo info = cm.getActiveNetworkInfo();
44+ if (info != null) {
45+ mIsConnected = (info.getState() == State.CONNECTED);
46+ }
47+
48+ mLockingWifi = lockingWifi;
49+
50+ final IntentFilter filter = new IntentFilter();
51+ filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
52+ manager.registerReceiver(this, filter);
53+ }
54+
55+ /* (non-Javadoc)
56+ * @see android.content.BroadcastReceiver#onReceive(android.content.Context, android.content.Intent)
57+ */
58+ @Override
59+ public void onReceive(Context context, Intent intent) {
60+ final String action = intent.getAction();
61+
62+ if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
63+ Log.w(TAG, "onReceived() called: " + intent);
64+ return;
65+ }
66+
67+ boolean noConnectivity = intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
68+ boolean isFailover = intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false);
69+
70+ Log.d(TAG, "onReceived() called; noConnectivity? " + noConnectivity + "; isFailover? " + isFailover);
71+
72+ if (noConnectivity && !isFailover && mIsConnected) {
73+ mIsConnected = false;
74+ mTerminalManager.onConnectivityLost();
75+ } else if (!mIsConnected) {
76+ NetworkInfo info = (NetworkInfo) intent.getExtras()
77+ .get(ConnectivityManager.EXTRA_NETWORK_INFO);
78+
79+ if (mIsConnected = (info.getState() == State.CONNECTED)) {
80+ mTerminalManager.onConnectivityRestored();
81+ }
82+ }
83+ }
84+
85+ /**
86+ *
87+ */
88+ public void cleanup() {
89+ if (mWifiLock.isHeld())
90+ mWifiLock.release();
91+
92+ mTerminalManager.unregisterReceiver(this);
93+ }
94+
95+ /**
96+ * Increase the number of things using the network. Acquire a Wifi lock if necessary.
97+ */
98+ public void incRef() {
99+ synchronized (this) {
100+ acquireWifiLockIfNecessary();
101+
102+ mNetworkRef += 1;
103+ }
104+ }
105+
106+ public void decRef() {
107+ synchronized (this) {
108+ mNetworkRef -= 1;
109+
110+ releaseWifiLockIfNecessary();
111+ }
112+ }
113+
114+ /**
115+ * @param mLockingWifi
116+ */
117+ public void setWantWifiLock(boolean lockingWifi) {
118+ synchronized (this) {
119+ mLockingWifi = lockingWifi;
120+
121+ if (mLockingWifi) {
122+ acquireWifiLockIfNecessary();
123+ } else if (!mLockingWifi) {
124+ releaseWifiLockIfNecessary();
125+ }
126+ }
127+ }
128+
129+ /**
130+ *
131+ */
132+ private void acquireWifiLockIfNecessary() {
133+ synchronized (this) {
134+ if (mLockingWifi && mNetworkRef > 0 && !mWifiLock.isHeld()) {
135+ mWifiLock.acquire();
136+ }
137+ }
138+ }
139+
140+ /**
141+ *
142+ */
143+ private void releaseWifiLockIfNecessary() {
144+ if (mNetworkRef == 0 && mWifiLock.isHeld()) {
145+ mWifiLock.release();
146+ }
147+ }
148+
149+ /**
150+ * @return whether we're connected to a network
151+ */
152+ public boolean isConnected() {
153+ return mIsConnected;
154+ }
155+}
--- a/src/org/connectbot/service/PromptHelper.java
+++ b/src/org/connectbot/service/PromptHelper.java
@@ -66,6 +66,9 @@ public class PromptHelper {
6666 */
6767 public void setResponse(Object value) {
6868 response = value;
69+ promptRequested = null;
70+ promptInstructions = null;
71+ promptHint = null;
6972 promptResponse.release();
7073 }
7174
@@ -101,9 +104,6 @@ public class PromptHelper {
101104
102105 // acquire lock until user passes back value
103106 promptResponse.acquire();
104- promptInstructions = null;
105- promptHint = null;
106- promptRequested = null;
107107
108108 response = popResponse();
109109 } finally {
--- a/src/org/connectbot/service/TerminalBridge.java
+++ b/src/org/connectbot/service/TerminalBridge.java
@@ -32,10 +32,8 @@ import org.connectbot.bean.SelectionArea;
3232 import org.connectbot.transport.AbsTransport;
3333 import org.connectbot.transport.TransportFactory;
3434 import org.connectbot.util.HostDatabase;
35-import org.connectbot.util.PreferenceConstants;
3635
3736 import android.content.Context;
38-import android.content.res.Configuration;
3937 import android.graphics.Bitmap;
4038 import android.graphics.Canvas;
4139 import android.graphics.Color;
@@ -45,10 +43,6 @@ import android.graphics.Bitmap.Config;
4543 import android.graphics.Paint.FontMetrics;
4644 import android.text.ClipboardManager;
4745 import android.util.Log;
48-import android.view.KeyCharacterMap;
49-import android.view.KeyEvent;
50-import android.view.View;
51-import android.view.View.OnKeyListener;
5246 import de.mud.terminal.VDUBuffer;
5347 import de.mud.terminal.VDUDisplay;
5448 import de.mud.terminal.vt320;
@@ -63,10 +57,11 @@ import de.mud.terminal.vt320;
6357 * This class also provides SSH hostkey verification prompting, and password
6458 * prompting.
6559 */
66-public class TerminalBridge implements VDUDisplay, OnKeyListener {
60+public class TerminalBridge implements VDUDisplay {
6761 public final static String TAG = "ConnectBot.TerminalBridge";
6862
6963 public final static int DEFAULT_FONT_SIZE = 10;
64+ private final static int FONT_SIZE_STEP = 2;
7065
7166 public Integer[] color;
7267
@@ -77,7 +72,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
7772
7873 public HostBean host;
7974
80- private AbsTransport transport;
75+ /* package */ AbsTransport transport;
8176
8277 final Paint defaultPaint;
8378
@@ -92,26 +87,6 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
9287 private TerminalView parent = null;
9388 private final Canvas canvas = new Canvas();
9489
95- private int metaState = 0;
96-
97- public final static int META_CTRL_ON = 0x01;
98- public final static int META_CTRL_LOCK = 0x02;
99- public final static int META_ALT_ON = 0x04;
100- public final static int META_ALT_LOCK = 0x08;
101- public final static int META_SHIFT_ON = 0x10;
102- public final static int META_SHIFT_LOCK = 0x20;
103- public final static int META_SLASH = 0x40;
104- public final static int META_TAB = 0x80;
105-
106- // The bit mask of momentary and lock states for each
107- public final static int META_CTRL_MASK = META_CTRL_ON | META_CTRL_LOCK;
108- public final static int META_ALT_MASK = META_ALT_ON | META_ALT_LOCK;
109- public final static int META_SHIFT_MASK = META_SHIFT_ON | META_SHIFT_LOCK;
110-
111- // All the transient key codes
112- public final static int META_TRANSIENT = META_CTRL_ON | META_ALT_ON
113- | META_SHIFT_ON;
114-
11590 private boolean disconnected = false;
11691 private boolean awaitingClose = false;
11792
@@ -119,16 +94,12 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
11994 private int columns;
12095 private int rows;
12196
122- private String keymode = null;
123-
124- private boolean hardKeyboard = false;
97+ /* package */ final TerminalKeyListener keyListener;
12598
12699 private boolean selectingForCopy = false;
127100 private final SelectionArea selectionArea;
128101 private ClipboardManager clipboard;
129102
130- protected KeyCharacterMap keymap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
131-
132103 public int charWidth = -1;
133104 public int charHeight = -1;
134105 private int charTop = -1;
@@ -179,6 +150,8 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
179150 fontSizeChangedListeners = new LinkedList<FontSizeChangedListener>();
180151
181152 transport = null;
153+
154+ keyListener = new TerminalKeyListener(manager, this, buffer, null);
182155 }
183156
184157 /**
@@ -187,7 +160,6 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
187160 * and password authentication.
188161 */
189162 public TerminalBridge(final TerminalManager manager, final HostBean host) throws IOException {
190-
191163 this.manager = manager;
192164 this.host = host;
193165
@@ -270,8 +242,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
270242
271243 selectionArea = new SelectionArea();
272244
273- hardKeyboard = (manager.res.getConfiguration().keyboard
274- == Configuration.KEYBOARD_QWERTY);
245+ keyListener = new TerminalKeyListener(manager, this, buffer, host.getEncoding());
275246 }
276247
277248 public PromptHelper getPromptHelper() {
@@ -336,6 +307,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
336307 public void setCharset(String encoding) {
337308 if (relay != null)
338309 relay.setCharset(encoding);
310+ keyListener.setCharset(encoding);
339311 }
340312
341313 /**
@@ -455,8 +427,12 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
455427 if (disconnectListener != null)
456428 disconnectListener.onDisconnected(TerminalBridge.this);
457429 } else {
430+ {
431+ final String line = manager.res.getString(R.string.alert_disconnect_msg);
432+ ((vt320) buffer).putString("\r\n" + line + "\r\n");
433+ }
458434 if (host.getStayConnected()) {
459- startConnection();
435+ manager.requestReconnect(this);
460436 return;
461437 }
462438 Thread disconnectPromptThread = new Thread(new Runnable() {
@@ -478,394 +454,6 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
478454 }
479455 }
480456
481- public void refreshKeymode() {
482- keymode = manager.getKeyMode();
483- }
484-
485- /**
486- * Handle onKey() events coming down from a {@link TerminalView} above us.
487- * Modify the keys to make more sense to a host then pass it to the transport.
488- */
489- public boolean onKey(View v, int keyCode, KeyEvent event) {
490- try {
491-
492- boolean hardKeyboardHidden =
493- manager.res.getConfiguration().hardKeyboardHidden ==
494- Configuration.HARDKEYBOARDHIDDEN_YES;
495-
496- // Ignore all key-up events except for the special keys
497- if (event.getAction() == KeyEvent.ACTION_UP) {
498- // There's nothing here for virtual keyboard users.
499- if (!hardKeyboard || (hardKeyboard && hardKeyboardHidden))
500- return false;
501-
502- // skip keys if we aren't connected yet or have been disconnected
503- if (disconnected || transport == null)
504- return false;
505-
506- if (PreferenceConstants.KEYMODE_RIGHT.equals(keymode)) {
507- if (keyCode == KeyEvent.KEYCODE_ALT_RIGHT
508- && (metaState & META_SLASH) != 0) {
509- metaState &= ~(META_SLASH | META_TRANSIENT);
510- transport.write('/');
511- return true;
512- } else if (keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT
513- && (metaState & META_TAB) != 0) {
514- metaState &= ~(META_TAB | META_TRANSIENT);
515- transport.write(0x09);
516- return true;
517- }
518- } else if (PreferenceConstants.KEYMODE_LEFT.equals(keymode)) {
519- if (keyCode == KeyEvent.KEYCODE_ALT_LEFT
520- && (metaState & META_SLASH) != 0) {
521- metaState &= ~(META_SLASH | META_TRANSIENT);
522- transport.write('/');
523- return true;
524- } else if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
525- && (metaState & META_TAB) != 0) {
526- metaState &= ~(META_TAB | META_TRANSIENT);
527- transport.write(0x09);
528- return true;
529- }
530- }
531-
532- return false;
533- }
534-
535- // check for terminal resizing keys
536- if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
537- forcedSize = false;
538- setFontSize(fontSize + 2);
539- return true;
540- } else if(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
541- forcedSize = false;
542- setFontSize(fontSize - 2);
543- return true;
544- }
545-
546- // skip keys if we aren't connected yet or have been disconnected
547- if (disconnected || transport == null)
548- return false;
549-
550- // if we're in scrollback, scroll to bottom of window on input
551- if (buffer.windowBase != buffer.screenBase)
552- buffer.setWindowBase(buffer.screenBase);
553-
554- boolean printing = (keymap.isPrintingKey(keyCode) || keyCode == KeyEvent.KEYCODE_SPACE);
555-
556- // otherwise pass through to existing session
557- // print normal keys
558- if (printing) {
559- int curMetaState = event.getMetaState();
560-
561- metaState &= ~(META_SLASH | META_TAB);
562-
563- if ((metaState & META_SHIFT_MASK) != 0) {
564- curMetaState |= KeyEvent.META_SHIFT_ON;
565- metaState &= ~META_SHIFT_ON;
566- redraw();
567- }
568-
569- if ((metaState & META_ALT_MASK) != 0) {
570- curMetaState |= KeyEvent.META_ALT_ON;
571- metaState &= ~META_ALT_ON;
572- redraw();
573- }
574-
575- int key = keymap.get(keyCode, curMetaState);
576-
577- if ((metaState & META_CTRL_MASK) != 0) {
578- metaState &= ~META_CTRL_ON;
579- redraw();
580-
581- if ((!hardKeyboard || (hardKeyboard && hardKeyboardHidden))
582- && sendFunctionKey(keyCode))
583- return true;
584-
585- // Support CTRL-a through CTRL-z
586- if (key >= 0x61 && key <= 0x7A)
587- key -= 0x60;
588- // Support CTRL-A through CTRL-_
589- else if (key >= 0x41 && key <= 0x5F)
590- key -= 0x40;
591- else if (key == 0x20)
592- key = 0x00;
593- else if (key == 0x3F)
594- key = 0x7F;
595- }
596-
597- // handle pressing f-keys
598- if ((hardKeyboard && !hardKeyboardHidden)
599- && (curMetaState & KeyEvent.META_SHIFT_ON) != 0
600- && sendFunctionKey(keyCode))
601- return true;
602-
603- if (key < 0x80)
604- transport.write(key);
605- else
606- // TODO write encoding routine that doesn't allocate each time
607- transport.write(new String(Character.toChars(key))
608- .getBytes(host.getEncoding()));
609-
610- return true;
611- }
612-
613- if (keyCode == KeyEvent.KEYCODE_UNKNOWN &&
614- event.getAction() == KeyEvent.ACTION_MULTIPLE) {
615- byte[] input = event.getCharacters().getBytes(host.getEncoding());
616- transport.write(input);
617- return true;
618- }
619-
620- // try handling keymode shortcuts
621- if (hardKeyboard && !hardKeyboardHidden &&
622- event.getRepeatCount() == 0) {
623- if (PreferenceConstants.KEYMODE_RIGHT.equals(keymode)) {
624- switch (keyCode) {
625- case KeyEvent.KEYCODE_ALT_RIGHT:
626- metaState |= META_SLASH;
627- return true;
628- case KeyEvent.KEYCODE_SHIFT_RIGHT:
629- metaState |= META_TAB;
630- return true;
631- case KeyEvent.KEYCODE_SHIFT_LEFT:
632- metaPress(META_SHIFT_ON);
633- return true;
634- case KeyEvent.KEYCODE_ALT_LEFT:
635- metaPress(META_ALT_ON);
636- return true;
637- }
638- } else if (PreferenceConstants.KEYMODE_LEFT.equals(keymode)) {
639- switch (keyCode) {
640- case KeyEvent.KEYCODE_ALT_LEFT:
641- metaState |= META_SLASH;
642- return true;
643- case KeyEvent.KEYCODE_SHIFT_LEFT:
644- metaState |= META_TAB;
645- return true;
646- case KeyEvent.KEYCODE_SHIFT_RIGHT:
647- metaPress(META_SHIFT_ON);
648- return true;
649- case KeyEvent.KEYCODE_ALT_RIGHT:
650- metaPress(META_ALT_ON);
651- return true;
652- }
653- } else {
654- switch (keyCode) {
655- case KeyEvent.KEYCODE_ALT_LEFT:
656- case KeyEvent.KEYCODE_ALT_RIGHT:
657- metaPress(META_ALT_ON);
658- return true;
659- case KeyEvent.KEYCODE_SHIFT_LEFT:
660- case KeyEvent.KEYCODE_SHIFT_RIGHT:
661- metaPress(META_SHIFT_ON);
662- return true;
663- }
664- }
665- }
666-
667- // look for special chars
668- switch(keyCode) {
669- case KeyEvent.KEYCODE_CAMERA:
670-
671- // check to see which shortcut the camera button triggers
672- String camera = manager.prefs.getString(
673- PreferenceConstants.CAMERA,
674- PreferenceConstants.CAMERA_CTRLA_SPACE);
675- if(PreferenceConstants.CAMERA_CTRLA_SPACE.equals(camera)) {
676- transport.write(0x01);
677- transport.write(' ');
678- } else if(PreferenceConstants.CAMERA_CTRLA.equals(camera)) {
679- transport.write(0x01);
680- } else if(PreferenceConstants.CAMERA_ESC.equals(camera)) {
681- ((vt320)buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0);
682- }
683-
684- break;
685-
686- case KeyEvent.KEYCODE_DEL:
687- ((vt320) buffer).keyPressed(vt320.KEY_BACK_SPACE, ' ',
688- getStateForBuffer());
689- metaState &= ~META_TRANSIENT;
690- return true;
691- case KeyEvent.KEYCODE_ENTER:
692- ((vt320)buffer).keyTyped(vt320.KEY_ENTER, ' ', 0);
693- metaState &= ~META_TRANSIENT;
694- return true;
695-
696- case KeyEvent.KEYCODE_DPAD_LEFT:
697- if (selectingForCopy) {
698- selectionArea.decrementColumn();
699- redraw();
700- } else {
701- ((vt320) buffer).keyPressed(vt320.KEY_LEFT, ' ',
702- getStateForBuffer());
703- metaState &= ~META_TRANSIENT;
704- tryKeyVibrate();
705- }
706- return true;
707-
708- case KeyEvent.KEYCODE_DPAD_UP:
709- if (selectingForCopy) {
710- selectionArea.decrementRow();
711- redraw();
712- } else {
713- ((vt320) buffer).keyPressed(vt320.KEY_UP, ' ',
714- getStateForBuffer());
715- metaState &= ~META_TRANSIENT;
716- tryKeyVibrate();
717- }
718- return true;
719-
720- case KeyEvent.KEYCODE_DPAD_DOWN:
721- if (selectingForCopy) {
722- selectionArea.incrementRow();
723- redraw();
724- } else {
725- ((vt320) buffer).keyPressed(vt320.KEY_DOWN, ' ',
726- getStateForBuffer());
727- metaState &= ~META_TRANSIENT;
728- tryKeyVibrate();
729- }
730- return true;
731-
732- case KeyEvent.KEYCODE_DPAD_RIGHT:
733- if (selectingForCopy) {
734- selectionArea.incrementColumn();
735- redraw();
736- } else {
737- ((vt320) buffer).keyPressed(vt320.KEY_RIGHT, ' ',
738- getStateForBuffer());
739- metaState &= ~META_TRANSIENT;
740- tryKeyVibrate();
741- }
742- return true;
743-
744- case KeyEvent.KEYCODE_DPAD_CENTER:
745- if (selectingForCopy) {
746- if (selectionArea.isSelectingOrigin())
747- selectionArea.finishSelectingOrigin();
748- else {
749- if (parent != null && clipboard != null) {
750- // copy selected area to clipboard
751- String copiedText = selectionArea.copyFrom(buffer);
752-
753- clipboard.setText(copiedText);
754- parent.notifyUser(parent.getContext().getString(
755- R.string.console_copy_done,
756- copiedText.length()));
757-
758- selectingForCopy = false;
759- selectionArea.reset();
760- }
761- }
762- } else {
763- if ((metaState & META_CTRL_ON) != 0) {
764- ((vt320)buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0);
765- metaState &= ~META_CTRL_ON;
766- } else
767- metaState |= META_CTRL_ON;
768- }
769-
770- redraw();
771-
772- return true;
773- }
774-
775- } catch (IOException e) {
776- Log.e(TAG, "Problem while trying to handle an onKey() event", e);
777- try {
778- transport.flush();
779- } catch (IOException ioe) {
780- Log.d(TAG, "Our transport was closed, dispatching disconnect event");
781- dispatchDisconnect(false);
782- }
783- } catch (NullPointerException npe) {
784- Log.d(TAG, "Input before connection established ignored.");
785- return true;
786- }
787-
788- return false;
789- }
790-
791- /**
792- * @param key
793- * @return successful
794- */
795- private boolean sendFunctionKey(int keyCode) {
796- switch (keyCode) {
797- case KeyEvent.KEYCODE_1:
798- ((vt320) buffer).keyPressed(vt320.KEY_F1, ' ', 0);
799- return true;
800- case KeyEvent.KEYCODE_2:
801- ((vt320) buffer).keyPressed(vt320.KEY_F2, ' ', 0);
802- return true;
803- case KeyEvent.KEYCODE_3:
804- ((vt320) buffer).keyPressed(vt320.KEY_F3, ' ', 0);
805- return true;
806- case KeyEvent.KEYCODE_4:
807- ((vt320) buffer).keyPressed(vt320.KEY_F4, ' ', 0);
808- return true;
809- case KeyEvent.KEYCODE_5:
810- ((vt320) buffer).keyPressed(vt320.KEY_F5, ' ', 0);
811- return true;
812- case KeyEvent.KEYCODE_6:
813- ((vt320) buffer).keyPressed(vt320.KEY_F6, ' ', 0);
814- return true;
815- case KeyEvent.KEYCODE_7:
816- ((vt320) buffer).keyPressed(vt320.KEY_F7, ' ', 0);
817- return true;
818- case KeyEvent.KEYCODE_8:
819- ((vt320) buffer).keyPressed(vt320.KEY_F8, ' ', 0);
820- return true;
821- case KeyEvent.KEYCODE_9:
822- ((vt320) buffer).keyPressed(vt320.KEY_F9, ' ', 0);
823- return true;
824- case KeyEvent.KEYCODE_0:
825- ((vt320) buffer).keyPressed(vt320.KEY_F10, ' ', 0);
826- return true;
827- default:
828- return false;
829- }
830- }
831-
832- /**
833- * Handle meta key presses where the key can be locked on.
834- * <p>
835- * 1st press: next key to have meta state<br />
836- * 2nd press: meta state is locked on<br />
837- * 3rd press: disable meta state
838- *
839- * @param code
840- */
841- private void metaPress(int code) {
842- if ((metaState & (code << 1)) != 0) {
843- metaState &= ~(code << 1);
844- } else if ((metaState & code) != 0) {
845- metaState &= ~code;
846- metaState |= code << 1;
847- } else
848- metaState |= code;
849- redraw();
850- }
851-
852- public int getMetaState() {
853- return metaState;
854- }
855-
856- private int getStateForBuffer() {
857- int bufferState = 0;
858-
859- if ((metaState & META_CTRL_MASK) != 0)
860- bufferState |= vt320.KEY_CONTROL;
861- if ((metaState & META_SHIFT_MASK) != 0)
862- bufferState |= vt320.KEY_SHIFT;
863- if ((metaState & META_ALT_MASK) != 0)
864- bufferState |= vt320.KEY_ALT;
865-
866- return bufferState;
867- }
868-
869457 public void setSelectingForCopy(boolean selectingForCopy) {
870458 this.selectingForCopy = selectingForCopy;
871459 }
@@ -886,7 +474,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
886474 * Request a different font size. Will make call to parentChanged() to make
887475 * sure we resize PTY if needed.
888476 */
889- private final void setFontSize(float size) {
477+ /* package */ final void setFontSize(float size) {
890478 if (size <= 0.0)
891479 return;
892480
@@ -911,6 +499,8 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
911499
912500 host.setFontSize((int) fontSize);
913501 manager.hostdb.updateFontSize(host);
502+
503+ forcedSize = false;
914504 }
915505
916506 /**
@@ -946,14 +536,15 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
946536 }
947537
948538 this.parent = parent;
949- int width = parent.getWidth();
950- int height = parent.getHeight();
539+ final int width = parent.getWidth();
540+ final int height = parent.getHeight();
951541
952542 // Something has gone wrong with our layout; we're 0 width or height!
953543 if (width <= 0 || height <= 0)
954544 return;
955545
956546 clipboard = (ClipboardManager) parent.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
547+ keyListener.setClipboardManager(clipboard);
957548
958549 if (!forcedSize) {
959550 // recalculate buffer size
@@ -1203,10 +794,10 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
1203794 if (direction > 0)
1204795 size -= step;
1205796
1206- forcedSize = true;
1207797 this.columns = cols;
1208798 this.rows = rows;
1209799 setFontSize(size);
800+ forcedSize = true;
1210801 }
1211802
1212803 private int fontSizeCompare(float size, int cols, int rows, int width, int height) {
@@ -1348,4 +939,41 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
1348939
1349940 return urls;
1350941 }
942+
943+ /**
944+ * @return
945+ */
946+ public boolean isUsingNetwork() {
947+ return transport.usesNetwork();
948+ }
949+
950+ /**
951+ * @return
952+ */
953+ public TerminalKeyListener getKeyHandler() {
954+ return keyListener;
955+ }
956+
957+ /**
958+ *
959+ */
960+ public void resetScrollPosition() {
961+ // if we're in scrollback, scroll to bottom of window on input
962+ if (buffer.windowBase != buffer.screenBase)
963+ buffer.setWindowBase(buffer.screenBase);
964+ }
965+
966+ /**
967+ *
968+ */
969+ public void increaseFontSize() {
970+ setFontSize(fontSize + FONT_SIZE_STEP);
971+ }
972+
973+ /**
974+ *
975+ */
976+ public void decreaseFontSize() {
977+ setFontSize(fontSize - FONT_SIZE_STEP);
978+ }
1351979 }
--- /dev/null
+++ b/src/org/connectbot/service/TerminalKeyListener.java
@@ -0,0 +1,490 @@
1+/**
2+ *
3+ */
4+package org.connectbot.service;
5+
6+import java.io.IOException;
7+
8+import org.connectbot.TerminalView;
9+import org.connectbot.bean.SelectionArea;
10+import org.connectbot.util.PreferenceConstants;
11+
12+import android.content.SharedPreferences;
13+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
14+import android.content.res.Configuration;
15+import android.preference.PreferenceManager;
16+import android.text.ClipboardManager;
17+import android.util.Log;
18+import android.view.KeyCharacterMap;
19+import android.view.KeyEvent;
20+import android.view.View;
21+import android.view.View.OnKeyListener;
22+import de.mud.terminal.VDUBuffer;
23+import de.mud.terminal.vt320;
24+
25+/**
26+ * @author kenny
27+ *
28+ */
29+public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceChangeListener {
30+ private static final String TAG = "ConnectBot.OnKeyListener";
31+
32+ public final static int META_CTRL_ON = 0x01;
33+ public final static int META_CTRL_LOCK = 0x02;
34+ public final static int META_ALT_ON = 0x04;
35+ public final static int META_ALT_LOCK = 0x08;
36+ public final static int META_SHIFT_ON = 0x10;
37+ public final static int META_SHIFT_LOCK = 0x20;
38+ public final static int META_SLASH = 0x40;
39+ public final static int META_TAB = 0x80;
40+
41+ // The bit mask of momentary and lock states for each
42+ public final static int META_CTRL_MASK = META_CTRL_ON | META_CTRL_LOCK;
43+ public final static int META_ALT_MASK = META_ALT_ON | META_ALT_LOCK;
44+ public final static int META_SHIFT_MASK = META_SHIFT_ON | META_SHIFT_LOCK;
45+
46+ // All the transient key codes
47+ public final static int META_TRANSIENT = META_CTRL_ON | META_ALT_ON
48+ | META_SHIFT_ON;
49+
50+ private final TerminalManager manager;
51+ private final TerminalBridge bridge;
52+ private final VDUBuffer buffer;
53+
54+ protected KeyCharacterMap keymap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
55+
56+ private String keymode = null;
57+ private boolean hardKeyboard = false;
58+
59+ private int metaState = 0;
60+
61+ private ClipboardManager clipboard = null;
62+ private boolean selectingForCopy = false;
63+ private final SelectionArea selectionArea;
64+
65+ private String encoding;
66+
67+ private final SharedPreferences prefs;
68+
69+ public TerminalKeyListener(TerminalManager manager,
70+ TerminalBridge bridge,
71+ VDUBuffer buffer,
72+ String encoding) {
73+ this.manager = manager;
74+ this.bridge = bridge;
75+ this.buffer = buffer;
76+ this.encoding = encoding;
77+
78+ selectionArea = new SelectionArea();
79+
80+ prefs = PreferenceManager.getDefaultSharedPreferences(manager);
81+ prefs.registerOnSharedPreferenceChangeListener(this);
82+
83+ hardKeyboard = (manager.res.getConfiguration().keyboard
84+ == Configuration.KEYBOARD_QWERTY);
85+
86+ updateKeymode();
87+ }
88+
89+ /**
90+ * Handle onKey() events coming down from a {@link TerminalView} above us.
91+ * Modify the keys to make more sense to a host then pass it to the transport.
92+ */
93+ public boolean onKey(View v, int keyCode, KeyEvent event) {
94+ try {
95+ final boolean hardKeyboardHidden = manager.hardKeyboardHidden;
96+
97+ // Ignore all key-up events except for the special keys
98+ if (event.getAction() == KeyEvent.ACTION_UP) {
99+ // There's nothing here for virtual keyboard users.
100+ if (!hardKeyboard || (hardKeyboard && hardKeyboardHidden))
101+ return false;
102+
103+ // skip keys if we aren't connected yet or have been disconnected
104+ if (bridge.isDisconnected() || bridge.transport == null)
105+ return false;
106+
107+ if (PreferenceConstants.KEYMODE_RIGHT.equals(keymode)) {
108+ if (keyCode == KeyEvent.KEYCODE_ALT_RIGHT
109+ && (metaState & META_SLASH) != 0) {
110+ metaState &= ~(META_SLASH | META_TRANSIENT);
111+ bridge.transport.write('/');
112+ return true;
113+ } else if (keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT
114+ && (metaState & META_TAB) != 0) {
115+ metaState &= ~(META_TAB | META_TRANSIENT);
116+ bridge.transport.write(0x09);
117+ return true;
118+ }
119+ } else if (PreferenceConstants.KEYMODE_LEFT.equals(keymode)) {
120+ if (keyCode == KeyEvent.KEYCODE_ALT_LEFT
121+ && (metaState & META_SLASH) != 0) {
122+ metaState &= ~(META_SLASH | META_TRANSIENT);
123+ bridge.transport.write('/');
124+ return true;
125+ } else if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
126+ && (metaState & META_TAB) != 0) {
127+ metaState &= ~(META_TAB | META_TRANSIENT);
128+ bridge.transport.write(0x09);
129+ return true;
130+ }
131+ }
132+
133+ return false;
134+ }
135+
136+ // check for terminal resizing keys
137+ if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
138+ bridge.increaseFontSize();
139+ return true;
140+ } else if(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
141+ bridge.decreaseFontSize();
142+ return true;
143+ }
144+
145+ // skip keys if we aren't connected yet or have been disconnected
146+ if (bridge.isDisconnected() || bridge.transport == null)
147+ return false;
148+
149+ bridge.resetScrollPosition();
150+
151+ boolean printing = (keymap.isPrintingKey(keyCode) || keyCode == KeyEvent.KEYCODE_SPACE);
152+
153+ // otherwise pass through to existing session
154+ // print normal keys
155+ if (printing) {
156+ int curMetaState = event.getMetaState();
157+
158+ metaState &= ~(META_SLASH | META_TAB);
159+
160+ if ((metaState & META_SHIFT_MASK) != 0) {
161+ curMetaState |= KeyEvent.META_SHIFT_ON;
162+ metaState &= ~META_SHIFT_ON;
163+ bridge.redraw();
164+ }
165+
166+ if ((metaState & META_ALT_MASK) != 0) {
167+ curMetaState |= KeyEvent.META_ALT_ON;
168+ metaState &= ~META_ALT_ON;
169+ bridge.redraw();
170+ }
171+
172+ int key = keymap.get(keyCode, curMetaState);
173+
174+ if ((metaState & META_CTRL_MASK) != 0) {
175+ metaState &= ~META_CTRL_ON;
176+ bridge.redraw();
177+
178+ if ((!hardKeyboard || (hardKeyboard && hardKeyboardHidden))
179+ && sendFunctionKey(keyCode))
180+ return true;
181+
182+ // Support CTRL-a through CTRL-z
183+ if (key >= 0x61 && key <= 0x7A)
184+ key -= 0x60;
185+ // Support CTRL-A through CTRL-_
186+ else if (key >= 0x41 && key <= 0x5F)
187+ key -= 0x40;
188+ else if (key == 0x20)
189+ key = 0x00;
190+ else if (key == 0x3F)
191+ key = 0x7F;
192+ }
193+
194+ // handle pressing f-keys
195+ if ((hardKeyboard && !hardKeyboardHidden)
196+ && (curMetaState & KeyEvent.META_SHIFT_ON) != 0
197+ && sendFunctionKey(keyCode))
198+ return true;
199+
200+ if (key < 0x80)
201+ bridge.transport.write(key);
202+ else
203+ // TODO write encoding routine that doesn't allocate each time
204+ bridge.transport.write(new String(Character.toChars(key))
205+ .getBytes(encoding));
206+
207+ return true;
208+ }
209+
210+ if (keyCode == KeyEvent.KEYCODE_UNKNOWN &&
211+ event.getAction() == KeyEvent.ACTION_MULTIPLE) {
212+ byte[] input = event.getCharacters().getBytes(encoding);
213+ bridge.transport.write(input);
214+ return true;
215+ }
216+
217+ // try handling keymode shortcuts
218+ if (hardKeyboard && !hardKeyboardHidden &&
219+ event.getRepeatCount() == 0) {
220+ if (PreferenceConstants.KEYMODE_RIGHT.equals(keymode)) {
221+ switch (keyCode) {
222+ case KeyEvent.KEYCODE_ALT_RIGHT:
223+ metaState |= META_SLASH;
224+ return true;
225+ case KeyEvent.KEYCODE_SHIFT_RIGHT:
226+ metaState |= META_TAB;
227+ return true;
228+ case KeyEvent.KEYCODE_SHIFT_LEFT:
229+ metaPress(META_SHIFT_ON);
230+ return true;
231+ case KeyEvent.KEYCODE_ALT_LEFT:
232+ metaPress(META_ALT_ON);
233+ return true;
234+ }
235+ } else if (PreferenceConstants.KEYMODE_LEFT.equals(keymode)) {
236+ switch (keyCode) {
237+ case KeyEvent.KEYCODE_ALT_LEFT:
238+ metaState |= META_SLASH;
239+ return true;
240+ case KeyEvent.KEYCODE_SHIFT_LEFT:
241+ metaState |= META_TAB;
242+ return true;
243+ case KeyEvent.KEYCODE_SHIFT_RIGHT:
244+ metaPress(META_SHIFT_ON);
245+ return true;
246+ case KeyEvent.KEYCODE_ALT_RIGHT:
247+ metaPress(META_ALT_ON);
248+ return true;
249+ }
250+ } else {
251+ switch (keyCode) {
252+ case KeyEvent.KEYCODE_ALT_LEFT:
253+ case KeyEvent.KEYCODE_ALT_RIGHT:
254+ metaPress(META_ALT_ON);
255+ return true;
256+ case KeyEvent.KEYCODE_SHIFT_LEFT:
257+ case KeyEvent.KEYCODE_SHIFT_RIGHT:
258+ metaPress(META_SHIFT_ON);
259+ return true;
260+ }
261+ }
262+ }
263+
264+ // look for special chars
265+ switch(keyCode) {
266+ case KeyEvent.KEYCODE_CAMERA:
267+
268+ // check to see which shortcut the camera button triggers
269+ String camera = manager.prefs.getString(
270+ PreferenceConstants.CAMERA,
271+ PreferenceConstants.CAMERA_CTRLA_SPACE);
272+ if(PreferenceConstants.CAMERA_CTRLA_SPACE.equals(camera)) {
273+ bridge.transport.write(0x01);
274+ bridge.transport.write(' ');
275+ } else if(PreferenceConstants.CAMERA_CTRLA.equals(camera)) {
276+ bridge.transport.write(0x01);
277+ } else if(PreferenceConstants.CAMERA_ESC.equals(camera)) {
278+ ((vt320)buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0);
279+ }
280+
281+ break;
282+
283+ case KeyEvent.KEYCODE_DEL:
284+ ((vt320) buffer).keyPressed(vt320.KEY_BACK_SPACE, ' ',
285+ getStateForBuffer());
286+ metaState &= ~META_TRANSIENT;
287+ return true;
288+ case KeyEvent.KEYCODE_ENTER:
289+ ((vt320)buffer).keyTyped(vt320.KEY_ENTER, ' ', 0);
290+ metaState &= ~META_TRANSIENT;
291+ return true;
292+
293+ case KeyEvent.KEYCODE_DPAD_LEFT:
294+ if (selectingForCopy) {
295+ selectionArea.decrementColumn();
296+ bridge.redraw();
297+ } else {
298+ ((vt320) buffer).keyPressed(vt320.KEY_LEFT, ' ',
299+ getStateForBuffer());
300+ metaState &= ~META_TRANSIENT;
301+ bridge.tryKeyVibrate();
302+ }
303+ return true;
304+
305+ case KeyEvent.KEYCODE_DPAD_UP:
306+ if (selectingForCopy) {
307+ selectionArea.decrementRow();
308+ bridge.redraw();
309+ } else {
310+ ((vt320) buffer).keyPressed(vt320.KEY_UP, ' ',
311+ getStateForBuffer());
312+ metaState &= ~META_TRANSIENT;
313+ bridge.tryKeyVibrate();
314+ }
315+ return true;
316+
317+ case KeyEvent.KEYCODE_DPAD_DOWN:
318+ if (selectingForCopy) {
319+ selectionArea.incrementRow();
320+ bridge.redraw();
321+ } else {
322+ ((vt320) buffer).keyPressed(vt320.KEY_DOWN, ' ',
323+ getStateForBuffer());
324+ metaState &= ~META_TRANSIENT;
325+ bridge.tryKeyVibrate();
326+ }
327+ return true;
328+
329+ case KeyEvent.KEYCODE_DPAD_RIGHT:
330+ if (selectingForCopy) {
331+ selectionArea.incrementColumn();
332+ bridge.redraw();
333+ } else {
334+ ((vt320) buffer).keyPressed(vt320.KEY_RIGHT, ' ',
335+ getStateForBuffer());
336+ metaState &= ~META_TRANSIENT;
337+ bridge.tryKeyVibrate();
338+ }
339+ return true;
340+
341+ case KeyEvent.KEYCODE_DPAD_CENTER:
342+ if (selectingForCopy) {
343+ if (selectionArea.isSelectingOrigin())
344+ selectionArea.finishSelectingOrigin();
345+ else {
346+ if (clipboard != null) {
347+ // copy selected area to clipboard
348+ String copiedText = selectionArea.copyFrom(buffer);
349+
350+ clipboard.setText(copiedText);
351+ // XXX STOPSHIP
352+// manager.notifyUser(manager.getString(
353+// R.string.console_copy_done,
354+// copiedText.length()));
355+
356+ selectingForCopy = false;
357+ selectionArea.reset();
358+ }
359+ }
360+ } else {
361+ if ((metaState & META_CTRL_ON) != 0) {
362+ ((vt320)buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0);
363+ metaState &= ~META_CTRL_ON;
364+ } else
365+ metaState |= META_CTRL_ON;
366+ }
367+
368+ bridge.redraw();
369+
370+ return true;
371+ }
372+
373+ } catch (IOException e) {
374+ Log.e(TAG, "Problem while trying to handle an onKey() event", e);
375+ try {
376+ bridge.transport.flush();
377+ } catch (IOException ioe) {
378+ Log.d(TAG, "Our transport was closed, dispatching disconnect event");
379+ bridge.dispatchDisconnect(false);
380+ }
381+ } catch (NullPointerException npe) {
382+ Log.d(TAG, "Input before connection established ignored.");
383+ return true;
384+ }
385+
386+ return false;
387+ }
388+
389+
390+ /**
391+ * @param key
392+ * @return successful
393+ */
394+ private boolean sendFunctionKey(int keyCode) {
395+ switch (keyCode) {
396+ case KeyEvent.KEYCODE_1:
397+ ((vt320) buffer).keyPressed(vt320.KEY_F1, ' ', 0);
398+ return true;
399+ case KeyEvent.KEYCODE_2:
400+ ((vt320) buffer).keyPressed(vt320.KEY_F2, ' ', 0);
401+ return true;
402+ case KeyEvent.KEYCODE_3:
403+ ((vt320) buffer).keyPressed(vt320.KEY_F3, ' ', 0);
404+ return true;
405+ case KeyEvent.KEYCODE_4:
406+ ((vt320) buffer).keyPressed(vt320.KEY_F4, ' ', 0);
407+ return true;
408+ case KeyEvent.KEYCODE_5:
409+ ((vt320) buffer).keyPressed(vt320.KEY_F5, ' ', 0);
410+ return true;
411+ case KeyEvent.KEYCODE_6:
412+ ((vt320) buffer).keyPressed(vt320.KEY_F6, ' ', 0);
413+ return true;
414+ case KeyEvent.KEYCODE_7:
415+ ((vt320) buffer).keyPressed(vt320.KEY_F7, ' ', 0);
416+ return true;
417+ case KeyEvent.KEYCODE_8:
418+ ((vt320) buffer).keyPressed(vt320.KEY_F8, ' ', 0);
419+ return true;
420+ case KeyEvent.KEYCODE_9:
421+ ((vt320) buffer).keyPressed(vt320.KEY_F9, ' ', 0);
422+ return true;
423+ case KeyEvent.KEYCODE_0:
424+ ((vt320) buffer).keyPressed(vt320.KEY_F10, ' ', 0);
425+ return true;
426+ default:
427+ return false;
428+ }
429+ }
430+
431+ /**
432+ * Handle meta key presses where the key can be locked on.
433+ * <p>
434+ * 1st press: next key to have meta state<br />
435+ * 2nd press: meta state is locked on<br />
436+ * 3rd press: disable meta state
437+ *
438+ * @param code
439+ */
440+ private void metaPress(int code) {
441+ if ((metaState & (code << 1)) != 0) {
442+ metaState &= ~(code << 1);
443+ } else if ((metaState & code) != 0) {
444+ metaState &= ~code;
445+ metaState |= code << 1;
446+ } else
447+ metaState |= code;
448+ bridge.redraw();
449+ }
450+
451+ public void setTerminalKeyMode(String keymode) {
452+ this.keymode = keymode;
453+ }
454+
455+ private int getStateForBuffer() {
456+ int bufferState = 0;
457+
458+ if ((metaState & META_CTRL_MASK) != 0)
459+ bufferState |= vt320.KEY_CONTROL;
460+ if ((metaState & META_SHIFT_MASK) != 0)
461+ bufferState |= vt320.KEY_SHIFT;
462+ if ((metaState & META_ALT_MASK) != 0)
463+ bufferState |= vt320.KEY_ALT;
464+
465+ return bufferState;
466+ }
467+
468+ public int getMetaState() {
469+ return metaState;
470+ }
471+
472+ public void setClipboardManager(ClipboardManager clipboard) {
473+ this.clipboard = clipboard;
474+ }
475+
476+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
477+ String key) {
478+ if (PreferenceConstants.KEYMODE.equals(key)) {
479+ updateKeymode();
480+ }
481+ }
482+
483+ private void updateKeymode() {
484+ keymode = prefs.getString(PreferenceConstants.KEYMODE, PreferenceConstants.KEYMODE_RIGHT);
485+ }
486+
487+ public void setCharset(String encoding) {
488+ this.encoding = encoding;
489+ }
490+}
--- a/src/org/connectbot/service/TerminalManager.java
+++ b/src/org/connectbot/service/TerminalManager.java
@@ -18,6 +18,7 @@
1818 package org.connectbot.service;
1919
2020 import java.io.IOException;
21+import java.lang.ref.WeakReference;
2122 import java.security.PrivateKey;
2223 import java.security.PublicKey;
2324 import java.util.Arrays;
@@ -44,14 +45,12 @@ import android.content.Intent;
4445 import android.content.SharedPreferences;
4546 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
4647 import android.content.res.AssetFileDescriptor;
48+import android.content.res.Configuration;
4749 import android.content.res.Resources;
4850 import android.media.AudioManager;
4951 import android.media.MediaPlayer;
5052 import android.media.MediaPlayer.OnCompletionListener;
51-import android.net.ConnectivityManager;
52-import android.net.NetworkInfo;
5353 import android.net.Uri;
54-import android.net.wifi.WifiManager;
5554 import android.os.Binder;
5655 import android.os.Handler;
5756 import android.os.IBinder;
@@ -73,6 +72,11 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
7372 public final static String TAG = "ConnectBot.TerminalManager";
7473
7574 public List<TerminalBridge> bridges = new LinkedList<TerminalBridge>();
75+ public Map<HostBean, WeakReference<TerminalBridge>> mHostBridgeMap =
76+ new HashMap<HostBean, WeakReference<TerminalBridge>>();
77+ public Map<String, WeakReference<TerminalBridge>> mNicknameBridgeMap =
78+ new HashMap<String, WeakReference<TerminalBridge>>();
79+
7680 public TerminalBridge defaultBridge = null;
7781
7882 public List<HostBean> disconnected = new LinkedList<HostBean>();
@@ -88,10 +92,9 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
8892
8993 protected SharedPreferences prefs;
9094
91- private final IBinder binder = new TerminalBinder();
95+ final private IBinder binder = new TerminalBinder();
9296
93- private ConnectivityManager connectivityManager;
94- private WifiManager.WifiLock wifilock;
97+ private ConnectivityReceiver connectivityManager;
9598
9699 private MediaPlayer mediaPlayer;
97100
@@ -108,6 +111,13 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
108111
109112 private boolean resizeAllowed = true;
110113
114+ private boolean savingKeys;
115+
116+ protected List<WeakReference<TerminalBridge>> mPendingReconnect
117+ = new LinkedList<WeakReference<TerminalBridge>>();
118+
119+ public boolean hardKeyboardHidden;
120+
111121 @Override
112122 public void onCreate() {
113123 Log.i(TAG, "Starting background service");
@@ -139,29 +149,31 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
139149 }
140150 }
141151
142- connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
143-
144- WifiManager manager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
145- wifilock = manager.createWifiLock(TAG);
146-
147152 vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
148153 wantKeyVibration = prefs.getBoolean(PreferenceConstants.BUMPY_ARROWS, true);
149154
150155 wantBellVibration = prefs.getBoolean(PreferenceConstants.BELL_VIBRATE, true);
151156 enableMediaPlayer();
157+
158+ hardKeyboardHidden = (res.getConfiguration().hardKeyboardHidden ==
159+ Configuration.HARDKEYBOARDHIDDEN_YES);
160+
161+ final boolean lockingWifi = prefs.getBoolean(PreferenceConstants.WIFI_LOCK, true);
162+
163+ connectivityManager = new ConnectivityReceiver(this, lockingWifi);
164+
165+ updateSavingKeys();
166+ }
167+
168+ private void updateSavingKeys() {
169+ savingKeys = prefs.getBoolean(PreferenceConstants.MEMKEYS, true);
152170 }
153171
154172 @Override
155173 public void onDestroy() {
156174 Log.i(TAG, "Destroying background service");
157175
158- if (bridges.size() > 0) {
159- TerminalBridge[] tmpBridges = bridges.toArray(new TerminalBridge[bridges.size()]);
160-
161- // disconnect and dispose of any existing bridges
162- for (int i = 0; i < tmpBridges.length; i++)
163- tmpBridges[i].dispatchDisconnect(true);
164- }
176+ disconnectAll(true);
165177
166178 if(hostdb != null) {
167179 hostdb.close();
@@ -180,8 +192,7 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
180192 pubkeyTimer.cancel();
181193 }
182194
183- if (wifilock != null && wifilock.isHeld())
184- wifilock.release();
195+ connectivityManager.cleanup();
185196
186197 ConnectionNotifier.getInstance().hideRunningNotification(this);
187198
@@ -189,30 +200,56 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
189200 }
190201
191202 /**
203+ * Disconnect all currently connected bridges.
204+ */
205+ private void disconnectAll(final boolean immediate) {
206+ TerminalBridge[] tmpBridges = null;
207+
208+ synchronized (bridges) {
209+ if (bridges.size() > 0) {
210+ tmpBridges = bridges.toArray(new TerminalBridge[bridges.size()]);
211+ }
212+ }
213+
214+ if (tmpBridges != null) {
215+ // disconnect and dispose of any existing bridges
216+ for (int i = 0; i < tmpBridges.length; i++)
217+ tmpBridges[i].dispatchDisconnect(immediate);
218+ }
219+ }
220+
221+ /**
192222 * Open a new SSH session using the given parameters.
193223 */
194- private void openConnection(HostBean host) throws IllegalArgumentException, IOException {
224+ private TerminalBridge openConnection(HostBean host) throws IllegalArgumentException, IOException {
195225 // throw exception if terminal already open
196- if (findBridge(host) != null) {
226+ if (getConnectedBridge(host) != null) {
197227 throw new IllegalArgumentException("Connection already open for that nickname");
198228 }
199229
200230 TerminalBridge bridge = new TerminalBridge(this, host);
201231 bridge.setOnDisconnectedListener(this);
202232 bridge.startConnection();
203- bridges.add(bridge);
204233
205- // Add a reference to the WifiLock
206- NetworkInfo info = connectivityManager.getActiveNetworkInfo();
207- if (isLockingWifi() &&
208- info != null &&
209- info.getType() == ConnectivityManager.TYPE_WIFI) {
210- Log.d(TAG, "Acquiring WifiLock");
211- wifilock.acquire();
234+ synchronized (bridges) {
235+ bridges.add(bridge);
236+ WeakReference<TerminalBridge> wr = new WeakReference<TerminalBridge>(bridge);
237+ mHostBridgeMap.put(bridge.host, wr);
238+ mNicknameBridgeMap.put(bridge.host.getNickname(), wr);
239+ }
240+
241+ synchronized (disconnected) {
242+ disconnected.remove(bridge.host);
243+ }
244+
245+ if (bridge.isUsingNetwork()) {
246+ connectivityManager.incRef();
212247 }
213248
214249 // also update database with new connected time
215250 touchHost(host);
251+
252+ return bridge;
216253 }
217254
218255 public String getEmulation() {
@@ -228,29 +265,17 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
228265 return scrollback;
229266 }
230267
231- public boolean isSavingKeys() {
232- return prefs.getBoolean(PreferenceConstants.MEMKEYS, true);
233- }
234-
235- public String getKeyMode() {
236- return prefs.getString(PreferenceConstants.KEYMODE, PreferenceConstants.KEYMODE_RIGHT); // "Use right-side keys"
237- }
238-
239- public boolean isLockingWifi() {
240- return prefs.getBoolean(PreferenceConstants.WIFI_LOCK, true);
241- }
242-
243268 /**
244269 * Open a new connection by reading parameters from the given URI. Follows
245270 * format specified by an individual transport.
246271 */
247- public void openConnection(Uri uri) throws Exception {
272+ public TerminalBridge openConnection(Uri uri) throws Exception {
248273 HostBean host = TransportFactory.findHost(hostdb, uri);
249274
250275 if (host == null)
251276 host = TransportFactory.getTransport(uri.getScheme()).createHost(uri);
252277
253- openConnection(host);
278+ return openConnection(host);
254279 }
255280
256281 /**
@@ -262,30 +287,57 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
262287 }
263288
264289 /**
265- * Find the {@link TerminalBridge} with the given nickname.
290+ * Find a connected {@link TerminalBridge} with the given HostBean.
291+ *
292+ * @param host the HostBean to search for
293+ * @return TerminalBridge that uses the HostBean
266294 */
267- public TerminalBridge findBridge(HostBean host) {
268- // find the first active bridge with given nickname
269- for(TerminalBridge bridge : bridges) {
270- if (bridge.host.equals(host))
271- return bridge;
295+ public TerminalBridge getConnectedBridge(HostBean host) {
296+ WeakReference<TerminalBridge> wr = mHostBridgeMap.get(host);
297+ if (wr != null) {
298+ return wr.get();
299+ } else {
300+ return null;
301+ }
302+ }
303+
304+ /**
305+ * Find a connected {@link TerminalBridge} using its nickname.
306+ *
307+ * @param nickname
308+ * @return TerminalBridge that matches nickname
309+ */
310+ public TerminalBridge getConnectedBridge(final String nickname) {
311+ if (nickname == null) {
312+ return null;
313+ }
314+ WeakReference<TerminalBridge> wr = mNicknameBridgeMap.get(nickname);
315+ if (wr != null) {
316+ return wr.get();
317+ } else {
318+ return null;
272319 }
273- return null;
274320 }
275321
276322 /**
277323 * Called by child bridge when somehow it's been disconnected.
278324 */
279325 public void onDisconnected(TerminalBridge bridge) {
280- // remove this bridge from our list
281- bridges.remove(bridge);
326+ synchronized (bridges) {
327+ // remove this bridge from our list
328+ bridges.remove(bridge);
329+
330+ mHostBridgeMap.remove(bridge.host);
331+ mNicknameBridgeMap.remove(bridge.host.getNickname());
282332
283- if (bridges.size() == 0 && wifilock.isHeld()) {
284- Log.d(TAG, "WifiLock was held, releasing");
285- wifilock.release();
333+ if (bridge.isUsingNetwork()) {
334+ connectivityManager.decRef();
335+ }
286336 }
287337
288- disconnected.add(bridge.host);
338+ synchronized (disconnected) {
339+ disconnected.add(bridge.host);
340+ }
289341
290342 // pass notification back up to gui
291343 if (disconnectHandler != null)
@@ -298,6 +350,9 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
298350 }
299351
300352 public void addKey(PubkeyBean pubkey, Object trileadKey) {
353+ if (!savingKeys)
354+ return;
355+
301356 removeKey(pubkey.getNickname());
302357
303358 byte[] sshPubKey = PubkeyUtils.extractOpenSSHPublic(trileadKey);
@@ -555,6 +610,11 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
555610 } else if (PreferenceConstants.BUMPY_ARROWS.equals(key)) {
556611 wantKeyVibration = sharedPreferences.getBoolean(
557612 PreferenceConstants.BUMPY_ARROWS, true);
613+ } else if (PreferenceConstants.WIFI_LOCK.equals(key)) {
614+ final boolean lockingWifi = prefs.getBoolean(PreferenceConstants.WIFI_LOCK, true);
615+ connectivityManager.setWantWifiLock(lockingWifi);
616+ } else if (PreferenceConstants.MEMKEYS.equals(key)) {
617+ updateSavingKeys();
558618 }
559619 }
560620
@@ -570,9 +630,69 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
570630 return resizeAllowed;
571631 }
572632
573- public class KeyHolder {
633+ public static class KeyHolder {
574634 public PubkeyBean bean;
575635 public Object trileadKey;
576636 public byte[] openSSHPubkey;
577637 }
638+
639+ /**
640+ * Called when connectivity to the network is lost and it doesn't appear
641+ * we'll be getting a different connection any time soon.
642+ */
643+ public void onConnectivityLost() {
644+ final Thread t = new Thread() {
645+ @Override
646+ public void run() {
647+ disconnectAll(false);
648+ }
649+ };
650+ t.start();
651+ }
652+
653+ /**
654+ * Called when connectivity to the network is restored.
655+ */
656+ public void onConnectivityRestored() {
657+ final Thread t = new Thread() {
658+ @Override
659+ public void run() {
660+ reconnectPending();
661+ }
662+ };
663+ t.start();
664+ }
665+
666+ /**
667+ * Insert request into reconnect queue to be executed either immediately
668+ * or later when connectivity is restored depending on whether we're
669+ * currently connected.
670+ *
671+ * @param bridge the TerminalBridge to reconnect when possible
672+ */
673+ public void requestReconnect(TerminalBridge bridge) {
674+ synchronized (mPendingReconnect) {
675+ mPendingReconnect.add(new WeakReference<TerminalBridge>(bridge));
676+ if (connectivityManager.isConnected()) {
677+ reconnectPending();
678+ }
679+ }
680+ }
681+
682+ /**
683+ * Reconnect all bridges that were pending a reconnect when connectivity
684+ * was lost.
685+ */
686+ private void reconnectPending() {
687+ synchronized (mPendingReconnect) {
688+ for (WeakReference<TerminalBridge> ref : mPendingReconnect) {
689+ TerminalBridge bridge = ref.get();
690+ if (bridge == null) {
691+ continue;
692+ }
693+ bridge.startConnection();
694+ }
695+ mPendingReconnect.clear();
696+ }
697+ }
578698 }
--- a/src/org/connectbot/transport/AbsTransport.java
+++ b/src/org/connectbot/transport/AbsTransport.java
@@ -246,4 +246,9 @@ public abstract class AbsTransport {
246246 public static String getFormatHint(Context context) {
247247 return "???";
248248 }
249+
250+ /**
251+ * @return
252+ */
253+ public abstract boolean usesNetwork();
249254 }
--- a/src/org/connectbot/transport/Local.java
+++ b/src/org/connectbot/transport/Local.java
@@ -208,4 +208,12 @@ public class Local extends AbsTransport {
208208 public static String getFormatHint(Context context) {
209209 return context.getString(R.string.hostpref_nickname_title);
210210 }
211+
212+ /* (non-Javadoc)
213+ * @see org.connectbot.transport.AbsTransport#usesNetwork()
214+ */
215+ @Override
216+ public boolean usesNetwork() {
217+ return false;
218+ }
211219 }
--- a/src/org/connectbot/transport/SSH.java
+++ b/src/org/connectbot/transport/SSH.java
@@ -299,7 +299,7 @@ public class SSH extends AbsTransport implements ConnectionMonitor, InteractiveC
299299 Log.d(TAG, String.format("Found unlocked key '%s' already in-memory", pubkey.getNickname()));
300300
301301 if (pubkey.isConfirmUse()) {
302- if (promptForPubkeyUse(pubkey.getNickname()))
302+ if (!promptForPubkeyUse(pubkey.getNickname()))
303303 return false;
304304 }
305305
@@ -342,10 +342,8 @@ public class SSH extends AbsTransport implements ConnectionMonitor, InteractiveC
342342
343343 Log.d(TAG, String.format("Unlocked key '%s'", pubkey.getNickname()));
344344
345- // save this key in-memory if option enabled
346- if(manager.isSavingKeys()) {
347- manager.addKey(pubkey, trileadKey);
348- }
345+ // save this key in memory
346+ manager.addKey(pubkey, trileadKey);
349347 }
350348
351349 return tryPublicKey(host.getUsername(), pubkey.getNickname(), trileadKey);
@@ -944,4 +942,12 @@ public class SSH extends AbsTransport implements ConnectionMonitor, InteractiveC
944942 agentLockPassphrase = lockPassphrase;
945943 return true;
946944 }
945+
946+ /* (non-Javadoc)
947+ * @see org.connectbot.transport.AbsTransport#usesNetwork()
948+ */
949+ @Override
950+ public boolean usesNetwork() {
951+ return true;
952+ }
947953 }
--- a/src/org/connectbot/transport/Telnet.java
+++ b/src/org/connectbot/transport/Telnet.java
@@ -319,4 +319,12 @@ public class Telnet extends AbsTransport {
319319 context.getString(R.string.format_hostname),
320320 context.getString(R.string.format_port));
321321 }
322+
323+ /* (non-Javadoc)
324+ * @see org.connectbot.transport.AbsTransport#usesNetwork()
325+ */
326+ @Override
327+ public boolean usesNetwork() {
328+ return true;
329+ }
322330 }
Show on old repository browser