diff options
Diffstat (limited to 'src')
29 files changed, 525 insertions, 242 deletions
diff --git a/src/com/nullwire/trace/ExceptionClickListener.java b/src/com/nullwire/trace/ExceptionClickListener.java index 3bf7adc..525b755 100644 --- a/src/com/nullwire/trace/ExceptionClickListener.java +++ b/src/com/nullwire/trace/ExceptionClickListener.java @@ -30,7 +30,7 @@ public class ExceptionClickListener implements OnClickListener { public void run() { ExceptionHandler.submitStackTraces(); } - }).run(); + }).start(); break; case DialogInterface.BUTTON_NEGATIVE: dialog.dismiss(); @@ -39,7 +39,7 @@ public class ExceptionClickListener implements OnClickListener { public void run() { ExceptionHandler.removeStackTraces(); } - }).run(); + }).start(); break; default: Log.d("ExceptionClickListener", "Got unknown button click: " + whichButton); diff --git a/src/com/trilead/ssh2/channel/DynamicAcceptThread.java b/src/com/trilead/ssh2/channel/DynamicAcceptThread.java index f3c8b07..ef3a3d0 100644 --- a/src/com/trilead/ssh2/channel/DynamicAcceptThread.java +++ b/src/com/trilead/ssh2/channel/DynamicAcceptThread.java @@ -202,7 +202,7 @@ public class DynamicAcceptThread extends Thread implements IChannelWorkerThread } try { - r2l = new StreamForwarder(cn, null, null, cn.stdoutStream, out, "RemoteToLocal"); + r2l = new StreamForwarder(cn, null, sock, cn.stdoutStream, out, "RemoteToLocal"); l2r = new StreamForwarder(cn, r2l, sock, in, cn.stdinStream, "LocalToRemote"); } catch (IOException e) { try { diff --git a/src/com/trilead/ssh2/channel/LocalAcceptThread.java b/src/com/trilead/ssh2/channel/LocalAcceptThread.java index 1b08d9c..885b41f 100644 --- a/src/com/trilead/ssh2/channel/LocalAcceptThread.java +++ b/src/com/trilead/ssh2/channel/LocalAcceptThread.java @@ -96,7 +96,7 @@ public class LocalAcceptThread extends Thread implements IChannelWorkerThread try
{
- r2l = new StreamForwarder(cn, null, null, cn.stdoutStream, s.getOutputStream(), "RemoteToLocal");
+ r2l = new StreamForwarder(cn, null, s, cn.stdoutStream, s.getOutputStream(), "RemoteToLocal");
l2r = new StreamForwarder(cn, r2l, s, s.getInputStream(), cn.stdinStream, "LocalToRemote");
}
catch (IOException e)
diff --git a/src/com/trilead/ssh2/channel/RemoteAcceptThread.java b/src/com/trilead/ssh2/channel/RemoteAcceptThread.java index 1ca9d76..8b97b55 100644 --- a/src/com/trilead/ssh2/channel/RemoteAcceptThread.java +++ b/src/com/trilead/ssh2/channel/RemoteAcceptThread.java @@ -52,7 +52,7 @@ public class RemoteAcceptThread extends Thread s = new Socket(targetAddress, targetPort);
- StreamForwarder r2l = new StreamForwarder(c, null, null, c.getStdoutStream(), s.getOutputStream(),
+ StreamForwarder r2l = new StreamForwarder(c, null, s, c.getStdoutStream(), s.getOutputStream(),
"RemoteToLocal");
StreamForwarder l2r = new StreamForwarder(c, null, null, s.getInputStream(), c.getStdinStream(),
"LocalToRemote");
diff --git a/src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java b/src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java index 8ee05a2..34ce51f 100644 --- a/src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java +++ b/src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java @@ -191,7 +191,7 @@ public class RemoteX11AcceptThread extends Thread /* Start forwarding traffic */
- StreamForwarder r2l = new StreamForwarder(c, null, null, remote_is, x11_os, "RemoteToX11");
+ StreamForwarder r2l = new StreamForwarder(c, null, s, remote_is, x11_os, "RemoteToX11");
StreamForwarder l2r = new StreamForwarder(c, null, null, x11_is, remote_os, "X11ToRemote");
/* No need to start two threads, one can be executed in the current thread */
diff --git a/src/com/trilead/ssh2/channel/StreamForwarder.java b/src/com/trilead/ssh2/channel/StreamForwarder.java index 376a3a0..7e54efb 100644 --- a/src/com/trilead/ssh2/channel/StreamForwarder.java +++ b/src/com/trilead/ssh2/channel/StreamForwarder.java @@ -17,13 +17,13 @@ import java.net.Socket; */
public class StreamForwarder extends Thread
{
- OutputStream os;
- InputStream is;
- byte[] buffer = new byte[Channel.CHANNEL_BUFFER_SIZE];
- Channel c;
- StreamForwarder sibling;
- Socket s;
- String mode;
+ final OutputStream os;
+ final InputStream is;
+ final byte[] buffer = new byte[Channel.CHANNEL_BUFFER_SIZE];
+ final Channel c;
+ final StreamForwarder sibling;
+ final Socket s;
+ final String mode;
StreamForwarder(Channel c, StreamForwarder sibling, Socket s, InputStream is, OutputStream os, String mode)
throws IOException
@@ -97,11 +97,12 @@ public class StreamForwarder extends Thread catch (IOException e3)
{
}
+ }
+ if (s != null) {
try
{
- if (s != null)
- s.close();
+ s.close();
}
catch (IOException e1)
{
@@ -109,4 +110,4 @@ public class StreamForwarder extends Thread }
}
}
-}
\ No newline at end of file +}
diff --git a/src/com/trilead/ssh2/compression/CompressionFactory.java b/src/com/trilead/ssh2/compression/CompressionFactory.java index a846f58..9f8d7ef 100644 --- a/src/com/trilead/ssh2/compression/CompressionFactory.java +++ b/src/com/trilead/ssh2/compression/CompressionFactory.java @@ -43,7 +43,7 @@ public class CompressionFactory { /* Higher Priority First */ compressors.addElement(new CompressorEntry("zlib", "com.trilead.ssh2.compression.Zlib")); - compressors.addElement(new CompressorEntry("zlib@openssh.com", "com.trilead.ssh2.compression.Zlib")); + compressors.addElement(new CompressorEntry("zlib@openssh.com", "com.trilead.ssh2.compression.ZlibOpenSSH")); compressors.addElement(new CompressorEntry("none", "")); } diff --git a/src/com/trilead/ssh2/compression/ICompressor.java b/src/com/trilead/ssh2/compression/ICompressor.java index 3f76326..0b435b9 100644 --- a/src/com/trilead/ssh2/compression/ICompressor.java +++ b/src/com/trilead/ssh2/compression/ICompressor.java @@ -27,4 +27,6 @@ public interface ICompressor { int compress(byte[] buf, int start, int len, byte[] output); byte[] uncompress(byte[] buf, int start, int[] len); + + boolean canCompressPreauth(); } diff --git a/src/com/trilead/ssh2/compression/Zlib.java b/src/com/trilead/ssh2/compression/Zlib.java index e9cc017..c1203a3 100644 --- a/src/com/trilead/ssh2/compression/Zlib.java +++ b/src/com/trilead/ssh2/compression/Zlib.java @@ -47,6 +47,10 @@ public class Zlib implements ICompressor { inflated_buf = new byte[DEFAULT_BUF_SIZE]; } + public boolean canCompressPreauth() { + return true; + } + public int getBufferSize() { return DEFAULT_BUF_SIZE; } diff --git a/src/com/trilead/ssh2/compression/ZlibOpenSSH.java b/src/com/trilead/ssh2/compression/ZlibOpenSSH.java new file mode 100644 index 0000000..266fff9 --- /dev/null +++ b/src/com/trilead/ssh2/compression/ZlibOpenSSH.java @@ -0,0 +1,35 @@ +/* + * ConnectBot: simple, powerful, open-source SSH client for Android + * Copyright 2007 Kenny Root, Jeffrey Sharkey + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.trilead.ssh2.compression; + +/** + * Defines how zlib@openssh.org compression works. + * See + * http://www.openssh.org/txt/draft-miller-secsh-compression-delayed-00.txt + * compression is disabled until userauth has occurred. + * + * @author Matt Johnston + * + */ +public class ZlibOpenSSH extends Zlib { + + public boolean canCompressPreauth() { + return false; + } + +} diff --git a/src/com/trilead/ssh2/transport/TransportConnection.java b/src/com/trilead/ssh2/transport/TransportConnection.java index 77eaded..2c04ce4 100644 --- a/src/com/trilead/ssh2/transport/TransportConnection.java +++ b/src/com/trilead/ssh2/transport/TransportConnection.java @@ -56,7 +56,9 @@ public class TransportConnection ICompressor send_comp = null;
- boolean can_compress = false;
+ boolean can_recv_compress = false;
+
+ boolean can_send_compress = false;
byte[] recv_comp_buffer;
@@ -117,16 +119,20 @@ public class TransportConnection {
recv_comp = comp;
- if (comp != null)
+ if (comp != null) {
recv_comp_buffer = new byte[comp.getBufferSize()];
+ can_recv_compress |= recv_comp.canCompressPreauth();
+ }
}
public void changeSendCompression(ICompressor comp)
{
send_comp = comp;
- if (comp != null)
+ if (comp != null) {
send_comp_buffer = new byte[comp.getBufferSize()];
+ can_send_compress |= send_comp.canCompressPreauth();
+ }
}
public void sendMessage(byte[] message) throws IOException
@@ -152,7 +158,7 @@ public class TransportConnection else if (padd > 64)
padd = 64;
- if (send_comp != null && can_compress) {
+ if (send_comp != null && can_send_compress) {
if (send_comp_buffer.length < message.length + 1024)
send_comp_buffer = new byte[message.length + 1024];
len = send_comp.compress(message, off, len, send_comp_buffer);
@@ -313,7 +319,7 @@ public class TransportConnection + " bytes payload");
}
- if (recv_comp != null && can_compress) {
+ if (recv_comp != null && can_recv_compress) {
int[] uncomp_len = new int[] { payload_length };
buffer = recv_comp.uncompress(buffer, off, uncomp_len);
@@ -331,6 +337,7 @@ public class TransportConnection *
*/
public void startCompression() {
- can_compress = true;
+ can_recv_compress = true;
+ can_send_compress = true;
}
}
diff --git a/src/de/mud/terminal/vt320.java b/src/de/mud/terminal/vt320.java index fd41a06..80f3856 100644 --- a/src/de/mud/terminal/vt320.java +++ b/src/de/mud/terminal/vt320.java @@ -25,6 +25,8 @@ package de.mud.terminal; +import android.text.AndroidCharacter; + import java.util.Properties; /** @@ -111,8 +113,11 @@ public abstract class vt320 extends VDUBuffer implements VDUInput { if (lastChar != -1) putChar((char) lastChar, isWide, false); lastChar = c; - if (fullwidths != null) - isWide = fullwidths[i] == 1; + if (fullwidths != null) { + final byte width = fullwidths[i]; + isWide = (width == AndroidCharacter.EAST_ASIAN_WIDTH_WIDE) + || (width == AndroidCharacter.EAST_ASIAN_WIDTH_FULL_WIDTH); + } } } } diff --git a/src/org/connectbot/ConsoleActivity.java b/src/org/connectbot/ConsoleActivity.java index 0240131..31ba444 100644 --- a/src/org/connectbot/ConsoleActivity.java +++ b/src/org/connectbot/ConsoleActivity.java @@ -23,6 +23,7 @@ import java.util.List; import org.connectbot.bean.SelectionArea; import org.connectbot.service.PromptHelper; import org.connectbot.service.TerminalBridge; +import org.connectbot.service.TerminalKeyListener; import org.connectbot.service.TerminalManager; import org.connectbot.util.PreferenceConstants; @@ -43,7 +44,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; -import android.os.PowerManager; import android.preference.PreferenceManager; import android.text.ClipboardManager; import android.util.Log; @@ -84,9 +84,9 @@ public class ConsoleActivity extends Activity { protected static final int REQUEST_EDIT = 1; - private static final int CLICK_TIME = 250; + private static final int CLICK_TIME = 400; private static final float MAX_CLICK_DISTANCE = 25f; - private static final int KEYBOARD_DISPLAY_TIME = 1250; + private static final int KEYBOARD_DISPLAY_TIME = 1500; // Direction to shift the ViewFlipper private static final int SHIFT_LEFT = 0; @@ -98,7 +98,9 @@ public class ConsoleActivity extends Activity { private SharedPreferences prefs = null; - private PowerManager.WakeLock wakelock = null; + // determines whether or not menuitem accelerators are bound + // otherwise they collide with an external keyboard's CTRL-char + private boolean hardKeyboard = false; protected Uri requested; @@ -116,7 +118,6 @@ public class ConsoleActivity extends Activity { private Animation slide_left_in, slide_left_out, slide_right_in, slide_right_out, fade_stay_hidden, fade_out_delayed; private Animation keyboard_fade_in, keyboard_fade_out; - private ImageView keyboardButton; private float lastX, lastY; private InputMethodManager inputManager; @@ -130,6 +131,8 @@ public class ConsoleActivity extends Activity { private Handler handler = new Handler(); + private ImageView mKeyboardButton; + private ServiceConnection connection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { bound = ((TerminalManager.TerminalBinder) service).getService(); @@ -256,9 +259,20 @@ public class ConsoleActivity extends Activity { booleanPromptGroup.setVisibility(View.GONE); } + // more like configureLaxMode -- enable network IO on UI thread + private void configureStrictMode() { + try { + Class.forName("android.os.StrictMode"); + StrictModeSetup.run(); + } catch (ClassNotFoundException e) { + } + } @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); + configureStrictMode(); + hardKeyboard = getResources().getConfiguration().keyboard == + Configuration.KEYBOARD_QWERTY; this.setContentView(R.layout.act_console); @@ -276,9 +290,6 @@ public class ConsoleActivity extends Activity { // TODO find proper way to disable volume key beep if it exists. setVolumeControlStream(AudioManager.STREAM_MUSIC); - PowerManager manager = (PowerManager)getSystemService(Context.POWER_SERVICE); - wakelock = manager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, TAG); - // handle requested console from incoming intent requested = getIntent().getData(); @@ -347,15 +358,46 @@ public class ConsoleActivity extends Activity { keyboard_fade_out = AnimationUtils.loadAnimation(this, R.anim.keyboard_fade_out); inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - keyboardButton = (ImageView) findViewById(R.id.keyboard_button); - keyboardButton.setOnClickListener(new OnClickListener() { + + final RelativeLayout keyboardGroup = (RelativeLayout) findViewById(R.id.keyboard_group); + + mKeyboardButton = (ImageView) findViewById(R.id.button_keyboard); + mKeyboardButton.setOnClickListener(new OnClickListener() { public void onClick(View view) { View flip = findCurrentView(R.id.console_flip); if (flip == null) return; inputManager.showSoftInput(flip, InputMethodManager.SHOW_FORCED); - keyboardButton.setVisibility(View.GONE); + keyboardGroup.setVisibility(View.GONE); + } + }); + + final ImageView ctrlButton = (ImageView) findViewById(R.id.button_ctrl); + ctrlButton.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + View flip = findCurrentView(R.id.console_flip); + if (flip == null) return; + TerminalView terminal = (TerminalView)flip; + + TerminalKeyListener handler = terminal.bridge.getKeyHandler(); + handler.metaPress(TerminalKeyListener.META_CTRL_ON); + + keyboardGroup.setVisibility(View.GONE); + } + }); + + final ImageView escButton = (ImageView) findViewById(R.id.button_esc); + escButton.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + View flip = findCurrentView(R.id.console_flip); + if (flip == null) return; + TerminalView terminal = (TerminalView)flip; + + TerminalKeyListener handler = terminal.bridge.getKeyHandler(); + handler.sendEscape(); + + keyboardGroup.setVisibility(View.GONE); } }); @@ -519,21 +561,20 @@ public class ConsoleActivity extends Activity { lastX = event.getX(); lastY = event.getY(); } else if (event.getAction() == MotionEvent.ACTION_UP - && config.hardKeyboardHidden != Configuration.KEYBOARDHIDDEN_NO - && keyboardButton.getVisibility() == View.GONE + && keyboardGroup.getVisibility() == View.GONE && event.getEventTime() - event.getDownTime() < CLICK_TIME && Math.abs(event.getX() - lastX) < MAX_CLICK_DISTANCE && Math.abs(event.getY() - lastY) < MAX_CLICK_DISTANCE) { - keyboardButton.startAnimation(keyboard_fade_in); - keyboardButton.setVisibility(View.VISIBLE); + keyboardGroup.startAnimation(keyboard_fade_in); + keyboardGroup.setVisibility(View.VISIBLE); handler.postDelayed(new Runnable() { public void run() { - if (keyboardButton.getVisibility() == View.GONE) + if (keyboardGroup.getVisibility() == View.GONE) return; - keyboardButton.startAnimation(keyboard_fade_out); - keyboardButton.setVisibility(View.GONE); + keyboardGroup.startAnimation(keyboard_fade_out); + keyboardGroup.setVisibility(View.GONE); } }, KEYBOARD_DISPLAY_TIME); } @@ -594,7 +635,8 @@ public class ConsoleActivity extends Activity { menu.setQwertyMode(true); disconnect = menu.add(R.string.list_host_disconnect); - disconnect.setAlphabeticShortcut('w'); + if (hardKeyboard) + disconnect.setAlphabeticShortcut('w'); if (!sessionOpen && disconnected) disconnect.setTitle(R.string.console_menu_close); disconnect.setEnabled(activeTerminal); @@ -611,7 +653,8 @@ public class ConsoleActivity extends Activity { }); copy = menu.add(R.string.console_menu_copy); - copy.setAlphabeticShortcut('c'); + if (hardKeyboard) + copy.setAlphabeticShortcut('c'); copy.setIcon(android.R.drawable.ic_menu_set_as); copy.setEnabled(activeTerminal); copy.setOnMenuItemClickListener(new OnMenuItemClickListener() { @@ -635,7 +678,8 @@ public class ConsoleActivity extends Activity { }); paste = menu.add(R.string.console_menu_paste); - paste.setAlphabeticShortcut('v'); + if (hardKeyboard) + paste.setAlphabeticShortcut('v'); paste.setIcon(android.R.drawable.ic_menu_edit); paste.setEnabled(clipboard.hasText() && sessionOpen); paste.setOnMenuItemClickListener(new OnMenuItemClickListener() { @@ -653,7 +697,8 @@ public class ConsoleActivity extends Activity { }); portForward = menu.add(R.string.console_menu_portforwards); - portForward.setAlphabeticShortcut('f'); + if (hardKeyboard) + portForward.setAlphabeticShortcut('f'); portForward.setIcon(android.R.drawable.ic_menu_manage); portForward.setEnabled(sessionOpen && canForwardPorts); portForward.setOnMenuItemClickListener(new OnMenuItemClickListener() { @@ -669,7 +714,8 @@ public class ConsoleActivity extends Activity { }); urlscan = menu.add(R.string.console_menu_urlscan); - urlscan.setAlphabeticShortcut('u'); + if (hardKeyboard) + urlscan.setAlphabeticShortcut('u'); urlscan.setIcon(android.R.drawable.ic_menu_search); urlscan.setEnabled(activeTerminal); urlscan.setOnMenuItemClickListener(new OnMenuItemClickListener() { @@ -694,7 +740,8 @@ public class ConsoleActivity extends Activity { }); resize = menu.add(R.string.console_menu_resize); - resize.setAlphabeticShortcut('s'); + if (hardKeyboard) + resize.setAlphabeticShortcut('s'); resize.setIcon(android.R.drawable.ic_menu_crop); resize.setEnabled(sessionOpen); resize.setOnMenuItemClickListener(new OnMenuItemClickListener() { @@ -785,10 +832,6 @@ public class ConsoleActivity extends Activity { super.onPause(); Log.d(TAG, "onPause called"); - // Allow the screen to dim and fall asleep. - if (wakelock != null && wakelock.isHeld()) - wakelock.release(); - if (forcedOrientation && bound != null) bound.setResizeAllowed(false); } @@ -800,8 +843,11 @@ public class ConsoleActivity extends Activity { // Make sure we don't let the screen fall asleep. // This also keeps the Wi-Fi chipset from disconnecting us. - if (wakelock != null && prefs.getBoolean(PreferenceConstants.KEEP_ALIVE, true)) - wakelock.acquire(); + if (prefs.getBoolean(PreferenceConstants.KEEP_ALIVE, true)) { + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } else { + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } configureOrientation(); @@ -843,6 +889,8 @@ public class ConsoleActivity extends Activity { requestedBridge = bound.openConnection(requested); } catch(Exception e) { Log.e(TAG, "Problem while trying to create new requested bridge from URI", e); + // TODO: We should display an error dialog here. + return; } requestedIndex = addNewTerminalView(requestedBridge); @@ -1006,6 +1054,8 @@ public class ConsoleActivity extends Activity { bound.setResizeAllowed(true); bound.hardKeyboardHidden = (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES); + + mKeyboardButton.setVisibility(bound.hardKeyboardHidden ? View.VISIBLE : View.GONE); } } diff --git a/src/org/connectbot/HostEditorActivity.java b/src/org/connectbot/HostEditorActivity.java index 95d04cc..4e8427f 100644 --- a/src/org/connectbot/HostEditorActivity.java +++ b/src/org/connectbot/HostEditorActivity.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.Map.Entry; import org.connectbot.bean.HostBean; @@ -130,6 +131,11 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr return true; } + // Gingerbread compatibility + public void apply() { + commit(); + } + public android.content.SharedPreferences.Editor putBoolean(String key, boolean value) { return this.putString(key, Boolean.toString(value)); } @@ -158,6 +164,9 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr return this; } + public android.content.SharedPreferences.Editor putStringSet(String key, Set<String> value) { + throw new UnsupportedOperationException("HostEditor Prefs do not support Set<String>"); + } } @@ -193,6 +202,10 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr return values.get(key); } + public Set<String> getStringSet(String key, Set<String> defValue) { + throw new ClassCastException("HostEditor Prefs do not support Set<String>"); + } + protected List<OnSharedPreferenceChangeListener> listeners = new LinkedList<OnSharedPreferenceChangeListener>(); public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) { diff --git a/src/org/connectbot/HostListActivity.java b/src/org/connectbot/HostListActivity.java index f65c069..3b02530 100644 --- a/src/org/connectbot/HostListActivity.java +++ b/src/org/connectbot/HostListActivity.java @@ -178,7 +178,8 @@ public class HostListActivity extends ListActivity { // start thread to check for new version new UpdateHelper(this); - this.makingShortcut = Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction()); + this.makingShortcut = Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction()) + || Intent.ACTION_PICK.equals(getIntent().getAction()); // connect with hosts database and populate list this.hostdb = new HostDatabase(this); diff --git a/src/org/connectbot/PortForwardListActivity.java b/src/org/connectbot/PortForwardListActivity.java index e5c6d21..f9982e4 100644 --- a/src/org/connectbot/PortForwardListActivity.java +++ b/src/org/connectbot/PortForwardListActivity.java @@ -113,7 +113,7 @@ public class PortForwardListActivity extends ListActivity { host = hostdb.findHostById(hostId); { - String nickname = host.getNickname(); + final String nickname = host != null ? host.getNickname() : null; final Resources resources = getResources(); if (nickname != null) { @@ -209,7 +209,8 @@ public class PortForwardListActivity extends ListActivity { break; } - PortForwardBean pfb = new PortForwardBean(host.getId(), + PortForwardBean pfb = new PortForwardBean( + host != null ? host.getId() : -1, nicknameEdit.getText().toString(), type, sourcePortEdit.getText().toString(), destEdit.getText().toString()); @@ -219,7 +220,7 @@ public class PortForwardListActivity extends ListActivity { hostBridge.enablePortForward(pfb); } - if (!hostdb.savePortForward(pfb)) + if (host != null && !hostdb.savePortForward(pfb)) throw new SQLException("Could not save port forward"); updateHandler.sendEmptyMessage(-1); diff --git a/src/org/connectbot/PubkeyListActivity.java b/src/org/connectbot/PubkeyListActivity.java index 9106e1a..3579980 100644 --- a/src/org/connectbot/PubkeyListActivity.java +++ b/src/org/connectbot/PubkeyListActivity.java @@ -293,7 +293,7 @@ public class PubkeyListActivity extends ListActivity implements EventListener { Log.d(TAG, String.format("Unlocked key '%s'", pubkey.getNickname())); // save this key in memory - bound.addKey(pubkey, trileadKey); + bound.addKey(pubkey, trileadKey, true); updateHandler.sendEmptyMessage(-1); } diff --git a/src/org/connectbot/StrictModeSetup.java b/src/org/connectbot/StrictModeSetup.java new file mode 100644 index 0000000..3a2000e --- /dev/null +++ b/src/org/connectbot/StrictModeSetup.java @@ -0,0 +1,23 @@ +/* + * ConnectBot: simple, powerful, open-source SSH client for Android + * Copyright 2007 Kenny Root, Jeffrey Sharkey + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.connectbot; +import android.os.StrictMode; +public class StrictModeSetup { + public static void run() { + StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.LAX); + } +} diff --git a/src/org/connectbot/TerminalView.java b/src/org/connectbot/TerminalView.java index 45f2977..e2f257c 100755 --- a/src/org/connectbot/TerminalView.java +++ b/src/org/connectbot/TerminalView.java @@ -39,6 +39,7 @@ import android.graphics.Path; import android.graphics.PixelXorXfermode; import android.graphics.RectF; import android.net.Uri; +import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.view.accessibility.AccessibilityEvent; @@ -203,6 +204,11 @@ public class TerminalView extends View implements FontSizeChangedListener { bridge.charHeight); canvas.drawPaint(cursorPaint); + final int deadKey = bridge.getKeyHandler().getDeadKey(); + if (deadKey != 0) { + canvas.drawText(new char[] { (char)deadKey }, 0, 1, 0, 0, cursorStrokePaint); + } + // Make sure we scale our decorations to the correct size. canvas.concat(scaleMatrix); @@ -291,7 +297,19 @@ public class TerminalView extends View implements FontSizeChangedListener { EditorInfo.IME_FLAG_NO_ENTER_ACTION | EditorInfo.IME_ACTION_NONE; outAttrs.inputType = EditorInfo.TYPE_NULL; - return new BaseInputConnection(this, false); + return new BaseInputConnection(this, false) { + @Override + public boolean deleteSurroundingText (int leftLength, int rightLength) { + if (rightLength == 0 && leftLength == 0) { + return this.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)); + } + for (int i = 0; i < leftLength; i++) { + this.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)); + } + // TODO: forward delete + return true; + } + }; } public StringBuffer getAccessibilityBuffer() { diff --git a/src/org/connectbot/service/ConnectionNotifier.java b/src/org/connectbot/service/ConnectionNotifier.java index 679ed9e..d276761 100644 --- a/src/org/connectbot/service/ConnectionNotifier.java +++ b/src/org/connectbot/service/ConnectionNotifier.java @@ -17,6 +17,9 @@ package org.connectbot.service; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + import org.connectbot.ConsoleActivity; import org.connectbot.R; import org.connectbot.bean.HostBean; @@ -41,7 +44,7 @@ public abstract class ConnectionNotifier { private static final int ONLINE_NOTIFICATION = 1; private static final int ACTIVITY_NOTIFICATION = 2; - public static ConnectionNotifier getInstance() { + public static ConnectionNotifier getInstance() { if (PreferenceConstants.PRE_ECLAIR) return PreEclair.Holder.sInstance; else @@ -128,20 +131,46 @@ public abstract class ConnectionNotifier { public abstract void hideRunningNotification(Service context); private static class PreEclair extends ConnectionNotifier { + private static final Class<?>[] setForegroundSignature = new Class[] {boolean.class}; + private Method setForeground = null; + private static class Holder { private static final PreEclair sInstance = new PreEclair(); } + public PreEclair() { + try { + setForeground = Service.class.getMethod("setForeground", setForegroundSignature); + } catch (Exception e) { + } + } + @Override public void showRunningNotification(Service context) { - context.setForeground(true); - getNotificationManager(context).notify(ONLINE_NOTIFICATION, newRunningNotification(context)); + if (setForeground != null) { + Object[] setForegroundArgs = new Object[1]; + setForegroundArgs[0] = Boolean.TRUE; + try { + setForeground.invoke(context, setForegroundArgs); + } catch (InvocationTargetException e) { + } catch (IllegalAccessException e) { + } + getNotificationManager(context).notify(ONLINE_NOTIFICATION, newRunningNotification(context)); + } } @Override public void hideRunningNotification(Service context) { - context.setForeground(false); - getNotificationManager(context).cancel(ONLINE_NOTIFICATION); + if (setForeground != null) { + Object[] setForegroundArgs = new Object[1]; + setForegroundArgs[0] = Boolean.FALSE; + try { + setForeground.invoke(context, setForegroundArgs); + } catch (InvocationTargetException e) { + } catch (IllegalAccessException e) { + } + getNotificationManager(context).cancel(ONLINE_NOTIFICATION); + } } } diff --git a/src/org/connectbot/service/ConnectivityReceiver.java b/src/org/connectbot/service/ConnectivityReceiver.java index e2d2d3b..3248a2a 100644 --- a/src/org/connectbot/service/ConnectivityReceiver.java +++ b/src/org/connectbot/service/ConnectivityReceiver.java @@ -31,6 +31,8 @@ public class ConnectivityReceiver extends BroadcastReceiver { private boolean mLockingWifi; + private Object[] mLock = new Object[0]; + public ConnectivityReceiver(TerminalManager manager, boolean lockingWifi) { mTerminalManager = manager; @@ -93,21 +95,26 @@ public class ConnectivityReceiver extends BroadcastReceiver { } /** - * Increase the number of things using the network. Acquire a Wifi lock if necessary. + * Increase the number of things using the network. Acquire a Wi-Fi lock + * if necessary. */ public void incRef() { - synchronized (this) { - acquireWifiLockIfNecessary(); - + synchronized (mLock) { mNetworkRef += 1; + + acquireWifiLockIfNecessaryLocked(); } } + /** + * Decrease the number of things using the network. Release the Wi-Fi lock + * if necessary. + */ public void decRef() { - synchronized (this) { + synchronized (mLock) { mNetworkRef -= 1; - releaseWifiLockIfNecessary(); + releaseWifiLockIfNecessaryLocked(); } } @@ -115,32 +122,24 @@ public class ConnectivityReceiver extends BroadcastReceiver { * @param mLockingWifi */ public void setWantWifiLock(boolean lockingWifi) { - synchronized (this) { + synchronized (mLock) { mLockingWifi = lockingWifi; if (mLockingWifi) { - acquireWifiLockIfNecessary(); - } else if (!mLockingWifi) { - releaseWifiLockIfNecessary(); + acquireWifiLockIfNecessaryLocked(); + } else { + releaseWifiLockIfNecessaryLocked(); } } } - /** - * - */ - private void acquireWifiLockIfNecessary() { - synchronized (this) { - if (mLockingWifi && mNetworkRef > 0 && !mWifiLock.isHeld()) { - mWifiLock.acquire(); - } + private void acquireWifiLockIfNecessaryLocked() { + if (mLockingWifi && mNetworkRef > 0 && !mWifiLock.isHeld()) { + mWifiLock.acquire(); } } - /** - * - */ - private void releaseWifiLockIfNecessary() { + private void releaseWifiLockIfNecessaryLocked() { if (mNetworkRef == 0 && mWifiLock.isHeld()) { mWifiLock.release(); } diff --git a/src/org/connectbot/service/Relay.java b/src/org/connectbot/service/Relay.java index f14ff53..36672ec 100644 --- a/src/org/connectbot/service/Relay.java +++ b/src/org/connectbot/service/Relay.java @@ -40,13 +40,10 @@ public class Relay implements Runnable { private static final int BUFFER_SIZE = 4096; - private static boolean useJNI = true; - private TerminalBridge bridge; private Charset currentCharset; private CharsetDecoder decoder; - private boolean isLegacyEastAsian = false; private AbsTransport transport; @@ -58,10 +55,6 @@ public class Relay implements Runnable { private byte[] byteArray; private char[] charArray; - static { - useJNI = EastAsianWidth.useJNI; - } - public Relay(TerminalBridge bridge, AbsTransport transport, vt320 buffer, String encoding) { setCharset(encoding); this.bridge = bridge; @@ -99,16 +92,9 @@ public class Relay implements Runnable { byteBuffer = ByteBuffer.allocate(BUFFER_SIZE); charBuffer = CharBuffer.allocate(BUFFER_SIZE); - /* for both JNI and non-JNI method */ + /* for East Asian character widths */ byte[] wideAttribute = new byte[BUFFER_SIZE]; - /* non-JNI fallback method */ - float[] widths = null; - - if (!useJNI) { - widths = new float[BUFFER_SIZE]; - } - byteArray = byteBuffer.array(); charArray = charBuffer.array(); @@ -120,6 +106,8 @@ public class Relay implements Runnable { int offset; int charWidth; + EastAsianWidth measurer = EastAsianWidth.getInstance(); + try { while (true) { charWidth = bridge.charWidth; @@ -143,15 +131,7 @@ public class Relay implements Runnable { offset = charBuffer.position(); - if (!useJNI) { - bridge.defaultPaint.getTextWidths(charArray, 0, offset, widths); - for (int i = 0; i < offset; i++) - wideAttribute[i] = - (byte) (((int)widths[i] != charWidth) ? 1 : 0); - } else { - EastAsianWidth.measure(charArray, 0, charBuffer.position(), - wideAttribute, isLegacyEastAsian); - } + measurer.measure(charArray, 0, offset, wideAttribute, bridge.defaultPaint, charWidth); buffer.putString(charArray, wideAttribute, 0, charBuffer.position()); bridge.propagateConsoleText(charArray, charBuffer.position()); charBuffer.clear(); diff --git a/src/org/connectbot/service/TerminalBridge.java b/src/org/connectbot/service/TerminalBridge.java index 9b4b96c..f25ba5d 100644 --- a/src/org/connectbot/service/TerminalBridge.java +++ b/src/org/connectbot/service/TerminalBridge.java @@ -57,6 +57,7 @@ import de.mud.terminal.vt320; * This class also provides SSH hostkey verification prompting, and password * prompting. */ +@SuppressWarnings("deprecation") // for ClipboardManager public class TerminalBridge implements VDUDisplay { public final static String TAG = "ConnectBot.TerminalBridge"; @@ -98,6 +99,8 @@ public class TerminalBridge implements VDUDisplay { private boolean selectingForCopy = false; private final SelectionArea selectionArea; + + // TODO add support for the new clipboard API private ClipboardManager clipboard; public int charWidth = -1; @@ -921,8 +924,6 @@ public class TerminalBridge implements VDUDisplay { color = manager.hostdb.getColorsForScheme(HostDatabase.DEFAULT_COLOR_SCHEME); } - // This was taken from http://geekswithblogs.net/casualjim/archive/2005/12/01/61722.aspx - private final static String urlRegex = "(?:(?:ht|f)tp(?:s?)\\:\\/\\/|~/|/)?(?:\\w+:\\w+@)?(?:(?:[-\\w]+\\.)+(?:com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum|travel|[a-z]{2}))(?::[\\d]{1,5})?(?:(?:(?:/(?:[-\\w~!$+|.,=]|%[a-f\\d]{2})+)+|/)+|\\?|#)?(?:(?:\\?(?:[-\\w~!$+|.,*:]|%[a-f\\d{2}])+=(?:[-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)(?:&(?:[-\\w~!$+|.,*:]|%[a-f\\d{2}])+=(?:[-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)*)*(?:#(?:[-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)?"; private static Pattern urlPattern = null; /** @@ -931,8 +932,36 @@ public class TerminalBridge implements VDUDisplay { public List<String> scanForURLs() { List<String> urls = new LinkedList<String>(); - if (urlPattern == null) - urlPattern = Pattern.compile(urlRegex); + if (urlPattern == null) { + // based on http://www.ietf.org/rfc/rfc2396.txt + String scheme = "[A-Za-z][-+.0-9A-Za-z]*"; + String unreserved = "[-._~0-9A-Za-z]"; + String pctEncoded = "%[0-9A-Fa-f]{2}"; + String subDelims = "[!$&'()*+,;:=]"; + String userinfo = "(?:" + unreserved + "|" + pctEncoded + "|" + subDelims + "|:)*"; + String h16 = "[0-9A-Fa-f]{1,4}"; + String decOctet = "(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])"; + String ipv4address = decOctet + "\\." + decOctet + "\\." + decOctet + "\\." + decOctet; + String ls32 = "(?:" + h16 + ":" + h16 + "|" + ipv4address + ")"; + String ipv6address = "(?:(?:" + h16 + "){6}" + ls32 + ")"; + String ipvfuture = "v[0-9A-Fa-f]+.(?:" + unreserved + "|" + subDelims + "|:)+"; + String ipLiteral = "\\[(?:" + ipv6address + "|" + ipvfuture + ")\\]"; + String regName = "(?:" + unreserved + "|" + pctEncoded + "|" + subDelims + ")*"; + String host = "(?:" + ipLiteral + "|" + ipv4address + "|" + regName + ")"; + String port = "[0-9]*"; + String authority = "(?:" + userinfo + "@)?" + host + "(?::" + port + ")?"; + String pchar = "(?:" + unreserved + "|" + pctEncoded + "|" + subDelims + "|@)"; + String segment = pchar + "*"; + String pathAbempty = "(?:/" + segment + ")*"; + String segmentNz = pchar + "+"; + String pathAbsolute = "/(?:" + segmentNz + "(?:/" + segment + ")*)?"; + String pathRootless = segmentNz + "(?:/" + segment + ")*"; + String hierPart = "(?://" + authority + pathAbempty + "|" + pathAbsolute + "|" + pathRootless + ")"; + String query = "(?:" + pchar + "|/|\\?)*"; + String fragment = "(?:" + pchar + "|/|\\?)*"; + String uriRegex = scheme + ":" + hierPart + "(?:" + query + ")?(?:#" + fragment + ")?"; + urlPattern = Pattern.compile(uriRegex); + } char[] visibleBuffer = new char[buffer.height * buffer.width]; for (int l = 0; l < buffer.height; l++) diff --git a/src/org/connectbot/service/TerminalKeyListener.java b/src/org/connectbot/service/TerminalKeyListener.java index af752bf..952f3cd 100644 --- a/src/org/connectbot/service/TerminalKeyListener.java +++ b/src/org/connectbot/service/TerminalKeyListener.java @@ -1,5 +1,18 @@ -/** +/* + * ConnectBot: simple, powerful, open-source SSH client for Android + * Copyright 2010 Kenny Root, Jeffrey Sharkey + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.connectbot.service; @@ -26,6 +39,7 @@ import de.mud.terminal.vt320; * @author kenny * */ +@SuppressWarnings("deprecation") // for ClipboardManager public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceChangeListener { private static final String TAG = "ConnectBot.OnKeyListener"; @@ -43,6 +57,10 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha public final static int META_ALT_MASK = META_ALT_ON | META_ALT_LOCK; public final static int META_SHIFT_MASK = META_SHIFT_ON | META_SHIFT_LOCK; + // backport constants from api level 11 + public final static int KEYCODE_ESCAPE = 111; + public final static int HC_META_CTRL_ON = 4096; + // All the transient key codes public final static int META_TRANSIENT = META_CTRL_ON | META_ALT_ON | META_SHIFT_ON; @@ -51,14 +69,16 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha private final TerminalBridge bridge; private final VDUBuffer buffer; - protected KeyCharacterMap keymap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD); - private String keymode = null; private boolean hardKeyboard = false; private int metaState = 0; + private int mDeadKey = 0; + + // TODO add support for the new API. private ClipboardManager clipboard = null; + private boolean selectingForCopy = false; private final SelectionArea selectionArea; @@ -148,47 +168,66 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha bridge.resetScrollPosition(); - boolean printing = (keymap.isPrintingKey(keyCode) || keyCode == KeyEvent.KEYCODE_SPACE); + if (keyCode == KeyEvent.KEYCODE_UNKNOWN && + event.getAction() == KeyEvent.ACTION_MULTIPLE) { + byte[] input = event.getCharacters().getBytes(encoding); + bridge.transport.write(input); + return true; + } + + int curMetaState = event.getMetaState(); + final int orgMetaState = curMetaState; + + if ((metaState & META_SHIFT_MASK) != 0) { + curMetaState |= KeyEvent.META_SHIFT_ON; + } + + if ((metaState & META_ALT_MASK) != 0) { + curMetaState |= KeyEvent.META_ALT_ON; + } + + int key = event.getUnicodeChar(curMetaState); + // no hard keyboard? ALT-k should pass through to below + if ((orgMetaState & KeyEvent.META_ALT_ON) != 0 && + (!hardKeyboard || hardKeyboardHidden)) { + key = 0; + } + + if ((key & KeyCharacterMap.COMBINING_ACCENT) != 0) { + mDeadKey = key & KeyCharacterMap.COMBINING_ACCENT_MASK; + return true; + } + + if (mDeadKey != 0) { + key = KeyCharacterMap.getDeadChar(mDeadKey, keyCode); + mDeadKey = 0; + } + + final boolean printing = (key != 0); // otherwise pass through to existing session // print normal keys if (printing) { - int curMetaState = event.getMetaState(); - metaState &= ~(META_SLASH | META_TAB); - if ((metaState & META_SHIFT_MASK) != 0) { - curMetaState |= KeyEvent.META_SHIFT_ON; - metaState &= ~META_SHIFT_ON; - bridge.redraw(); - } - - if ((metaState & META_ALT_MASK) != 0) { - curMetaState |= KeyEvent.META_ALT_ON; - metaState &= ~META_ALT_ON; + // Remove shift and alt modifiers + final int lastMetaState = metaState; + metaState &= ~(META_SHIFT_ON | META_ALT_ON); + if (metaState != lastMetaState) { bridge.redraw(); } - int key = keymap.get(keyCode, curMetaState); - if ((metaState & META_CTRL_MASK) != 0) { metaState &= ~META_CTRL_ON; bridge.redraw(); + // If there is no hard keyboard or there is a hard keyboard currently hidden, + // CTRL-1 through CTRL-9 will send F1 through F9 if ((!hardKeyboard || (hardKeyboard && hardKeyboardHidden)) && sendFunctionKey(keyCode)) return true; - // Support CTRL-a through CTRL-z - if (key >= 0x61 && key <= 0x7A) - key -= 0x60; - // Support CTRL-A through CTRL-_ - else if (key >= 0x41 && key <= 0x5F) - key -= 0x40; - else if (key == 0x20) - key = 0x00; - else if (key == 0x3F) - key = 0x7F; + key = keyAsControl(key); } // handle pressing f-keys @@ -207,13 +246,30 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha return true; } - if (keyCode == KeyEvent.KEYCODE_UNKNOWN && - event.getAction() == KeyEvent.ACTION_MULTIPLE) { - byte[] input = event.getCharacters().getBytes(encoding); - bridge.transport.write(input); - return true; + // send ctrl and meta-keys as appropriate + if (!hardKeyboard || hardKeyboardHidden) { + int k = event.getUnicodeChar(0); + int k0 = k; + boolean sendCtrl = false; + boolean sendMeta = false; + if (k != 0) { + if ((orgMetaState & HC_META_CTRL_ON) != 0) { + k = keyAsControl(k); + if (k != k0) + sendCtrl = true; + // send F1-F10 via CTRL-1 through CTRL-0 + if (!sendCtrl && sendFunctionKey(keyCode)) + return true; + } else if ((orgMetaState & KeyEvent.META_ALT_ON) != 0) { + sendMeta = true; + sendEscape(); + } + if (sendMeta || sendCtrl) { + bridge.transport.write(k); + return true; + } + } } - // try handling keymode shortcuts if (hardKeyboard && !hardKeyboardHidden && event.getRepeatCount() == 0) { @@ -263,6 +319,12 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha // look for special chars switch(keyCode) { + case KEYCODE_ESCAPE: + sendEscape(); + return true; + case KeyEvent.KEYCODE_TAB: + bridge.transport.write(0x09); + return true; case KeyEvent.KEYCODE_CAMERA: // check to see which shortcut the camera button triggers @@ -362,10 +424,10 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha } } else { if ((metaState & META_CTRL_ON) != 0) { - ((vt320)buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0); + sendEscape(); metaState &= ~META_CTRL_ON; } else - metaState |= META_CTRL_ON; + metaPress(META_CTRL_ON); } bridge.redraw(); @@ -389,6 +451,25 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha return false; } + public int keyAsControl(int key) { + // Support CTRL-a through CTRL-z + if (key >= 0x61 && key <= 0x7A) + key -= 0x60; + // Support CTRL-A through CTRL-_ + else if (key >= 0x41 && key <= 0x5F) + key -= 0x40; + // CTRL-space sends NULL + else if (key == 0x20) + key = 0x00; + // CTRL-? sends DEL + else if (key == 0x3F) + key = 0x7F; + return key; + } + + public void sendEscape() { + ((vt320)buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0); + } /** * @param key @@ -440,7 +521,7 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha * * @param code */ - private void metaPress(int code) { + public void metaPress(int code) { if ((metaState & (code << 1)) != 0) { metaState &= ~(code << 1); } else if ((metaState & code) != 0) { @@ -472,6 +553,10 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha return metaState; } + public int getDeadKey() { + return mDeadKey; + } + public void setClipboardManager(ClipboardManager clipboard) { this.clipboard = clipboard; } diff --git a/src/org/connectbot/service/TerminalManager.java b/src/org/connectbot/service/TerminalManager.java index c378b14..86a30f5 100644 --- a/src/org/connectbot/service/TerminalManager.java +++ b/src/org/connectbot/service/TerminalManager.java @@ -135,6 +135,7 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen pubkeydb = new PubkeyDatabase(this); // load all marked pubkeys into memory + updateSavingKeys(); List<PubkeyBean> pubkeys = pubkeydb.getAllStartPubkeys(); for (PubkeyBean pubkey : pubkeys) { @@ -162,7 +163,6 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen connectivityManager = new ConnectivityReceiver(this, lockingWifi); - updateSavingKeys(); } private void updateSavingKeys() { @@ -246,6 +246,10 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen connectivityManager.incRef(); } + if (prefs.getBoolean(PreferenceConstants.CONNECTION_PERSIST, true)) { + ConnectionNotifier.getInstance().showRunningNotification(this); + } + // also update database with new connected time touchHost(host); @@ -323,6 +327,8 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen * Called by child bridge when somehow it's been disconnected. */ public void onDisconnected(TerminalBridge bridge) { + boolean shouldHideRunningNotification = false; + synchronized (bridges) { // remove this bridge from our list bridges.remove(bridge); @@ -333,16 +339,24 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen if (bridge.isUsingNetwork()) { connectivityManager.decRef(); } + + if (bridges.size() == 0 && + mPendingReconnect.size() == 0) { + shouldHideRunningNotification = true; + } } synchronized (disconnected) { disconnected.add(bridge.host); } + if (shouldHideRunningNotification) { + ConnectionNotifier.getInstance().hideRunningNotification(this); + } + // pass notification back up to gui if (disconnectHandler != null) Message.obtain(disconnectHandler, -1, bridge).sendToTarget(); - } public boolean isKeyLoaded(String nickname) { @@ -350,7 +364,11 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen } public void addKey(PubkeyBean pubkey, Object trileadKey) { - if (!savingKeys) + addKey(pubkey, trileadKey, false); + } + + public void addKey(PubkeyBean pubkey, Object trileadKey, boolean force) { + if (!savingKeys && !force) return; removeKey(pubkey.getNickname()); @@ -465,8 +483,6 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen setResizeAllowed(true); - ConnectionNotifier.getInstance().hideRunningNotification(this); - stopIdleTimer(); // Make sure we stay running to maintain the bridges @@ -476,13 +492,20 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen } @Override + public int onStartCommand(Intent intent, int flags, int startId) { + /* + * We want this service to continue running until it is explicitly + * stopped, so return sticky. + */ + return START_STICKY; + } + + @Override public void onRebind(Intent intent) { super.onRebind(intent); setResizeAllowed(true); - ConnectionNotifier.getInstance().hideRunningNotification(this); - Log.i(TAG, "Someone rebound to TerminalManager"); stopIdleTimer(); @@ -496,13 +519,6 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen if (bridges.size() == 0) { stopWithDelay(); - } else { - /* If user wants the connections to stay alive at all costs, - * set the service to be "foreground." - */ - if (prefs.getBoolean(PreferenceConstants.CONNECTION_PERSIST, true)) { - ConnectionNotifier.getInstance().showRunningNotification(this); - } } return true; @@ -565,7 +581,7 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen vibrate(); } - class BeepListener implements OnCompletionListener { + private static class BeepListener implements OnCompletionListener { public void onCompletion(MediaPlayer mp) { mp.seekTo(0); } @@ -647,6 +663,7 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen disconnectAll(false); } }; + t.setName("Disconnector"); t.start(); } @@ -660,6 +677,7 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen reconnectPending(); } }; + t.setName("Reconnector"); t.start(); } @@ -673,7 +691,8 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen public void requestReconnect(TerminalBridge bridge) { synchronized (mPendingReconnect) { mPendingReconnect.add(new WeakReference<TerminalBridge>(bridge)); - if (connectivityManager.isConnected()) { + if (!bridge.isUsingNetwork() || + connectivityManager.isConnected()) { reconnectPending(); } } diff --git a/src/org/connectbot/util/EastAsianWidth.java b/src/org/connectbot/util/EastAsianWidth.java index b27cd48..0e274b5 100644 --- a/src/org/connectbot/util/EastAsianWidth.java +++ b/src/org/connectbot/util/EastAsianWidth.java @@ -17,42 +17,59 @@ package org.connectbot.util; -import android.util.Log; +import android.graphics.Paint; +import android.text.AndroidCharacter; /** * @author Kenny Root * */ -public class EastAsianWidth { - public static boolean useJNI = false; - private static final String TAG = "ConnectBot.EastAsianWidth"; +public abstract class EastAsianWidth { + public static EastAsianWidth getInstance() { + if (PreferenceConstants.PRE_FROYO) + return PreFroyo.Holder.sInstance; + else + return FroyoAndBeyond.Holder.sInstance; + } /** * @param charArray * @param i * @param position * @param wideAttribute - * @param isLegacyEastAsian */ - public native static void measure(char[] charArray, int start, int end, - byte[] wideAttribute, boolean isLegacyEastAsian); - - static { - try { - System.loadLibrary("org_connectbot_util_EastAsianWidth"); - - char[] testInput = {(char)0x4EBA}; - byte[] testResult = new byte[1]; - measure(testInput, 0, 1, testResult, true); - - if (testResult[0] == 1) - useJNI = true; - else - Log.d(TAG, "EastAsianWidth JNI measuring not available"); - } catch (Exception e) { - // Failure - } catch (UnsatisfiedLinkError e1) { - // Failure + public abstract void measure(char[] charArray, int start, int end, + byte[] wideAttribute, Paint paint, int charWidth); + + private static class PreFroyo extends EastAsianWidth { + private static final int BUFFER_SIZE = 4096; + private float[] mWidths = new float[BUFFER_SIZE]; + + private static class Holder { + private static final PreFroyo sInstance = new PreFroyo(); + } + + @Override + public void measure(char[] charArray, int start, int end, + byte[] wideAttribute, Paint paint, int charWidth) { + paint.getTextWidths(charArray, start, end, mWidths); + final int N = end - start; + for (int i = 0; i < N; i++) + wideAttribute[i] = (byte) (((int)mWidths[i] != charWidth) ? + AndroidCharacter.EAST_ASIAN_WIDTH_WIDE : + AndroidCharacter.EAST_ASIAN_WIDTH_NARROW); + } + } + + private static class FroyoAndBeyond extends EastAsianWidth { + private static class Holder { + private static final FroyoAndBeyond sInstance = new FroyoAndBeyond(); + } + + @Override + public void measure(char[] charArray, int start, int end, + byte[] wideAttribute, Paint paint, int charWidth) { + AndroidCharacter.getEastAsianWidths(charArray, start, end - start, wideAttribute); } } } diff --git a/src/org/connectbot/util/HostDatabase.java b/src/org/connectbot/util/HostDatabase.java index b0dce6d..2a92bab 100644 --- a/src/org/connectbot/util/HostDatabase.java +++ b/src/org/connectbot/util/HostDatabase.java @@ -124,6 +124,9 @@ public class HostDatabase extends RobustSQLiteOpenHelper { "CREATE INDEX " + TABLE_COLOR_DEFAULTS + FIELD_COLOR_SCHEME + "index ON " + TABLE_COLOR_DEFAULTS + " (" + FIELD_COLOR_SCHEME + ");"; + private static final String WHERE_SCHEME_AND_COLOR = FIELD_COLOR_SCHEME + " = ? AND " + + FIELD_COLOR_NUMBER + " = ?"; + static { addTableName(TABLE_HOSTS); addTableName(TABLE_PORTFORWARDS); @@ -273,8 +276,6 @@ public class HostDatabase extends RobustSQLiteOpenHelper { SQLiteDatabase db = this.getWritableDatabase(); db.update(TABLE_HOSTS, values, "_id = ?", new String[] { String.valueOf(host.getId()) }); - - db.close(); } } @@ -288,8 +289,6 @@ public class HostDatabase extends RobustSQLiteOpenHelper { SQLiteDatabase db = this.getWritableDatabase(); id = db.insert(TABLE_HOSTS, null, host.getValues()); - - db.close(); } host.setId(id); @@ -314,7 +313,6 @@ public class HostDatabase extends RobustSQLiteOpenHelper { db.update(TABLE_HOSTS, updates, "_id = ?", new String[] { String.valueOf(id) }); - db.close(); } return true; @@ -330,7 +328,6 @@ public class HostDatabase extends RobustSQLiteOpenHelper { synchronized (dbLock) { SQLiteDatabase db = this.getWritableDatabase(); db.delete(TABLE_HOSTS, "_id = ?", new String[] { String.valueOf(host.getId()) }); - db.close(); } } @@ -350,7 +347,6 @@ public class HostDatabase extends RobustSQLiteOpenHelper { hosts = createHostBeans(c); c.close(); - db.close(); } return hosts; @@ -473,8 +469,6 @@ public class HostDatabase extends RobustSQLiteOpenHelper { null, null, null); host = getFirstHostBean(c); - - db.close(); } return host; @@ -495,8 +489,6 @@ public class HostDatabase extends RobustSQLiteOpenHelper { null, null, null); host = getFirstHostBean(c); - - db.close(); } return host; @@ -521,8 +513,6 @@ public class HostDatabase extends RobustSQLiteOpenHelper { FIELD_HOST_HOSTNAME + " = ? AND " + FIELD_HOST_PORT + " = ?", new String[] { hostname, String.valueOf(port) }); Log.d(TAG, String.format("Finished saving hostkey information for '%s'", hostname)); - - db.close(); } } @@ -563,8 +553,6 @@ public class HostDatabase extends RobustSQLiteOpenHelper { c.close(); } - - db.close(); } return known; @@ -584,7 +572,6 @@ public class HostDatabase extends RobustSQLiteOpenHelper { SQLiteDatabase db = this.getWritableDatabase(); db.update(TABLE_HOSTS, values, FIELD_HOST_PUBKEYID + " = ?", new String[] { String.valueOf(pubkeyId) }); - db.close(); } Log.d(TAG, String.format("Set all hosts using pubkey id %d to -1", pubkeyId)); @@ -624,7 +611,6 @@ public class HostDatabase extends RobustSQLiteOpenHelper { } c.close(); - db.close(); } return portForwards; @@ -649,8 +635,6 @@ public class HostDatabase extends RobustSQLiteOpenHelper { if (db.update(TABLE_PORTFORWARDS, pfb.getValues(), "_id = ?", new String[] { String.valueOf(pfb.getId()) }) > 0) success = true; } - - db.close(); } return success; @@ -667,7 +651,6 @@ public class HostDatabase extends RobustSQLiteOpenHelper { synchronized (dbLock) { SQLiteDatabase db = this.getWritableDatabase(); db.delete(TABLE_PORTFORWARDS, "_id = ?", new String[] { String.valueOf(pfb.getId()) }); - db.close(); } } @@ -688,52 +671,38 @@ public class HostDatabase extends RobustSQLiteOpenHelper { } c.close(); - db.close(); } return colors; } public void setColorForScheme(int scheme, int number, int value) { - SQLiteDatabase db; + final SQLiteDatabase db; - String schemeWhere; - schemeWhere = FIELD_COLOR_SCHEME + " = ?"; + final String[] whereArgs = new String[] { String.valueOf(scheme), String.valueOf(number) }; if (value == Colors.defaults[number]) { - String[] whereArgs = new String[1]; - - whereArgs[0] = String.valueOf(number); - synchronized (dbLock) { db = getWritableDatabase(); db.delete(TABLE_COLORS, - FIELD_COLOR_NUMBER + " = ? AND " - + schemeWhere, - new String[] { String.valueOf(number) }); - - db.close(); + WHERE_SCHEME_AND_COLOR, whereArgs); } } else { - ContentValues values = new ContentValues(); - values.put(FIELD_COLOR_NUMBER, number); + final ContentValues values = new ContentValues(); values.put(FIELD_COLOR_VALUE, value); - String[] whereArgs = null; - - whereArgs = new String[] { String.valueOf(scheme) }; - synchronized (dbLock) { db = getWritableDatabase(); - int rowsAffected = db.update(TABLE_COLORS, values, - schemeWhere, whereArgs); + + final int rowsAffected = db.update(TABLE_COLORS, values, + WHERE_SCHEME_AND_COLOR, whereArgs); if (rowsAffected == 0) { + values.put(FIELD_COLOR_SCHEME, scheme); + values.put(FIELD_COLOR_NUMBER, number); db.insert(TABLE_COLORS, null, values); } - - db.close(); } } } @@ -760,8 +729,6 @@ public class HostDatabase extends RobustSQLiteOpenHelper { } c.close(); - - db.close(); } return colors; @@ -794,8 +761,6 @@ public class HostDatabase extends RobustSQLiteOpenHelper { values.put(FIELD_COLOR_SCHEME, scheme); db.insert(TABLE_COLOR_DEFAULTS, null, values); } - - db.close(); } } } diff --git a/src/org/connectbot/util/PreferenceConstants.java b/src/org/connectbot/util/PreferenceConstants.java index 37e1871..9e37017 100644 --- a/src/org/connectbot/util/PreferenceConstants.java +++ b/src/org/connectbot/util/PreferenceConstants.java @@ -26,7 +26,7 @@ import android.os.Build; public class PreferenceConstants { public static final boolean PRE_ECLAIR = (Integer.parseInt(Build.VERSION.SDK) <= 4); public static final boolean PRE_FROYO = PRE_ECLAIR ? true : - (Build.VERSION.SDK_INT <= 7); + (Integer.parseInt(Build.VERSION.SDK) <= 7); public static final String MEMKEYS = "memkeys"; public static final String UPDATE = "update"; diff --git a/src/org/connectbot/util/UberColorPickerDialog.java b/src/org/connectbot/util/UberColorPickerDialog.java index e12307e..2c01b30 100644 --- a/src/org/connectbot/util/UberColorPickerDialog.java +++ b/src/org/connectbot/util/UberColorPickerDialog.java @@ -65,8 +65,8 @@ import android.view.View; * @author Keith Wiley, kwiley@keithwiley.com, http://keithwiley.com */ public class UberColorPickerDialog extends Dialog { - private OnColorChangedListener mListener; - private int mInitialColor; + private final OnColorChangedListener mListener; + private final int mInitialColor; /** * Callback to the creator of the dialog, informing the creator of a new color and notifying that the dialog is about to dismiss. |