aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/trilead/ssh2/crypto/KeyMaterial.java9
-rw-r--r--src/com/trilead/ssh2/crypto/dh/DhGroupExchange.java6
-rw-r--r--src/com/trilead/ssh2/crypto/dh/GenericDhExchange.java9
-rw-r--r--src/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java11
-rw-r--r--src/com/trilead/ssh2/crypto/digest/MAC.java57
-rw-r--r--src/com/trilead/ssh2/signature/ECDSASHA2Verify.java4
-rw-r--r--src/com/trilead/ssh2/transport/KexManager.java21
-rw-r--r--src/org/connectbot/ConsoleActivity.java81
-rw-r--r--src/org/connectbot/GeneratePubkeyActivity.java7
-rw-r--r--src/org/connectbot/HostListActivity.java2
-rw-r--r--src/org/connectbot/service/KeyEventUtil.java149
-rw-r--r--src/org/connectbot/service/TerminalKeyListener.java12
-rw-r--r--src/org/connectbot/util/PreferenceConstants.java1
13 files changed, 290 insertions, 79 deletions
diff --git a/src/com/trilead/ssh2/crypto/KeyMaterial.java b/src/com/trilead/ssh2/crypto/KeyMaterial.java
index 499422f..1dbd6c7 100644
--- a/src/com/trilead/ssh2/crypto/KeyMaterial.java
+++ b/src/com/trilead/ssh2/crypto/KeyMaterial.java
@@ -3,8 +3,6 @@ package com.trilead.ssh2.crypto;
import java.math.BigInteger;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
@@ -74,12 +72,7 @@ public class KeyMaterial
{
KeyMaterial km = new KeyMaterial();
- HashForSSH2Types sh;
- try {
- sh = new HashForSSH2Types(MessageDigest.getInstance(hashAlgo));
- } catch (NoSuchAlgorithmException e) {
- throw new IllegalArgumentException(e);
- }
+ HashForSSH2Types sh = new HashForSSH2Types(hashAlgo);
km.initial_iv_client_to_server = calculateKey(sh, K, H, (byte) 'A', SessionID, blockSizeCS);
diff --git a/src/com/trilead/ssh2/crypto/dh/DhGroupExchange.java b/src/com/trilead/ssh2/crypto/dh/DhGroupExchange.java
index 2922284..a888950 100644
--- a/src/com/trilead/ssh2/crypto/dh/DhGroupExchange.java
+++ b/src/com/trilead/ssh2/crypto/dh/DhGroupExchange.java
@@ -87,10 +87,10 @@ public class DhGroupExchange
this.k = f.modPow(x, p);
}
- public byte[] calculateH(byte[] clientversion, byte[] serverversion, byte[] clientKexPayload,
- byte[] serverKexPayload, byte[] hostKey, DHGexParameters para)
+ public byte[] calculateH(String hashAlgo, byte[] clientversion, byte[] serverversion,
+ byte[] clientKexPayload, byte[] serverKexPayload, byte[] hostKey, DHGexParameters para)
{
- HashForSSH2Types hash = new HashForSSH2Types("SHA1");
+ HashForSSH2Types hash = new HashForSSH2Types(hashAlgo);
hash.updateByteString(clientversion);
hash.updateByteString(serverversion);
diff --git a/src/com/trilead/ssh2/crypto/dh/GenericDhExchange.java b/src/com/trilead/ssh2/crypto/dh/GenericDhExchange.java
index d65490a..039ff75 100644
--- a/src/com/trilead/ssh2/crypto/dh/GenericDhExchange.java
+++ b/src/com/trilead/ssh2/crypto/dh/GenericDhExchange.java
@@ -4,8 +4,6 @@ package com.trilead.ssh2.crypto.dh;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
import com.trilead.ssh2.log.Logger;
@@ -71,12 +69,7 @@ public abstract class GenericDhExchange
public byte[] calculateH(byte[] clientversion, byte[] serverversion, byte[] clientKexPayload,
byte[] serverKexPayload, byte[] hostKey) throws UnsupportedEncodingException
{
- HashForSSH2Types hash;
- try {
- hash = new HashForSSH2Types(MessageDigest.getInstance(getHashAlgo()));
- } catch (NoSuchAlgorithmException e) {
- throw new UnsupportedOperationException(e);
- }
+ HashForSSH2Types hash = new HashForSSH2Types(getHashAlgo());
if (log.isEnabled())
{
diff --git a/src/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java b/src/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java
index 9127d4e..6b0d6e3 100644
--- a/src/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java
+++ b/src/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java
@@ -16,19 +16,10 @@ public class HashForSSH2Types
{
MessageDigest md;
- public HashForSSH2Types(MessageDigest md)
- {
- this.md = md;
- }
-
public HashForSSH2Types(String type)
{
try {
- if ("SHA1".equals(type) || "MD5".equals(type)) {
- md = MessageDigest.getInstance(type);
- } else {
- throw new IllegalArgumentException("Unknown algorithm " + type);
- }
+ md = MessageDigest.getInstance(type);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Unsupported algorithm " + type);
}
diff --git a/src/com/trilead/ssh2/crypto/digest/MAC.java b/src/com/trilead/ssh2/crypto/digest/MAC.java
index 20c52fa..561599c 100644
--- a/src/com/trilead/ssh2/crypto/digest/MAC.java
+++ b/src/com/trilead/ssh2/crypto/digest/MAC.java
@@ -16,6 +16,36 @@ import javax.crypto.spec.SecretKeySpec;
*/
public final class MAC
{
+ /**
+ * From http://tools.ietf.org/html/rfc4253
+ */
+ private static final String HMAC_MD5 = "hmac-md5";
+
+ /**
+ * From http://tools.ietf.org/html/rfc4253
+ */
+ private static final String HMAC_MD5_96 = "hmac-md5-96";
+
+ /**
+ * From http://tools.ietf.org/html/rfc4253
+ */
+ private static final String HMAC_SHA1 = "hmac-sha1";
+
+ /**
+ * From http://tools.ietf.org/html/rfc4253
+ */
+ private static final String HMAC_SHA1_96 = "hmac-sha1-96";
+
+ /**
+ * From http://tools.ietf.org/html/rfc6668
+ */
+ private static final String HMAC_SHA2_256 = "hmac-sha2-256";
+
+ /**
+ * From http://tools.ietf.org/html/rfc6668
+ */
+ private static final String HMAC_SHA2_512 = "hmac-sha2-512";
+
Mac mac;
int outSize;
int macSize;
@@ -23,7 +53,8 @@ public final class MAC
/* Higher Priority First */
private static final String[] MAC_LIST = {
- "hmac-sha1-96", "hmac-sha1", "hmac-md5-96", "hmac-md5"
+ HMAC_SHA2_256, HMAC_SHA2_512,
+ HMAC_SHA1_96, HMAC_SHA1, HMAC_MD5_96, HMAC_MD5
};
public final static String[] getMacList()
@@ -39,28 +70,36 @@ public final class MAC
public final static int getKeyLen(String type)
{
- if (type.equals("hmac-sha1"))
+ if (HMAC_SHA1.equals(type) || HMAC_SHA1_96.equals(type))
return 20;
- if (type.equals("hmac-sha1-96"))
- return 20;
- if (type.equals("hmac-md5"))
- return 16;
- if (type.equals("hmac-md5-96"))
+ if (HMAC_MD5.equals(type) || HMAC_MD5_96.equals(type))
return 16;
+ if (HMAC_SHA2_256.equals(type))
+ return 32;
+ if (HMAC_SHA2_512.equals(type))
+ return 64;
throw new IllegalArgumentException("Unkown algorithm " + type);
}
public MAC(String type, byte[] key)
{
try {
- if ("hmac-sha1".equals(type) || "hmac-sha1-96".equals(type))
+ if (HMAC_SHA1.equals(type) || HMAC_SHA1_96.equals(type))
{
mac = Mac.getInstance("HmacSHA1");
}
- else if ("hmac-md5".equals(type) || "hmac-md5-96".equals(type))
+ else if (HMAC_MD5.equals(type) || HMAC_MD5_96.equals(type))
{
mac = Mac.getInstance("HmacMD5");
}
+ else if (HMAC_SHA2_256.equals(type))
+ {
+ mac = Mac.getInstance("HmacSHA256");
+ }
+ else if (HMAC_SHA2_512.equals(type))
+ {
+ mac = Mac.getInstance("HmacSHA512");
+ }
else
throw new IllegalArgumentException("Unkown algorithm " + type);
} catch (NoSuchAlgorithmException e) {
diff --git a/src/com/trilead/ssh2/signature/ECDSASHA2Verify.java b/src/com/trilead/ssh2/signature/ECDSASHA2Verify.java
index 49028e6..f139cdf 100644
--- a/src/com/trilead/ssh2/signature/ECDSASHA2Verify.java
+++ b/src/com/trilead/ssh2/signature/ECDSASHA2Verify.java
@@ -118,11 +118,11 @@ public class ECDSASHA2Verify {
KeyFactory kf = KeyFactory.getInstance("EC");
return (ECPublicKey) kf.generatePublic(keySpec);
} catch (NoSuchAlgorithmException nsae) {
- IOException ioe = new IOException("No RSA KeyFactory available");
+ IOException ioe = new IOException("No EC KeyFactory available");
ioe.initCause(nsae);
throw ioe;
} catch (InvalidKeySpecException ikse) {
- IOException ioe = new IOException("No RSA KeyFactory available");
+ IOException ioe = new IOException("No EC KeyFactory available");
ioe.initCause(ikse);
throw ioe;
}
diff --git a/src/com/trilead/ssh2/transport/KexManager.java b/src/com/trilead/ssh2/transport/KexManager.java
index 230047e..cd26530 100644
--- a/src/com/trilead/ssh2/transport/KexManager.java
+++ b/src/com/trilead/ssh2/transport/KexManager.java
@@ -61,6 +61,7 @@ public class KexManager
KEX_ALGS.add("ecdh-sha2-nistp256");
KEX_ALGS.add("ecdh-sha2-nistp384");
KEX_ALGS.add("ecdh-sha2-nistp521");
+ KEX_ALGS.add("diffie-hellman-group-exchange-sha256");
KEX_ALGS.add("diffie-hellman-group-exchange-sha1");
KEX_ALGS.add("diffie-hellman-group14-sha1");
KEX_ALGS.add("diffie-hellman-group1-sha1");
@@ -449,20 +450,24 @@ public class KexManager
ignore_next_kex_packet = true;
}
- if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1"))
+ if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1")
+ || kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha256"))
{
if (kxs.dhgexParameters.getMin_group_len() == 0 || csh.server_versioncomment.matches("OpenSSH_2\\.([0-4]\\.|5\\.[0-2]).*"))
{
PacketKexDhGexRequestOld dhgexreq = new PacketKexDhGexRequestOld(kxs.dhgexParameters);
tm.sendKexMessage(dhgexreq.getPayload());
-
}
else
{
PacketKexDhGexRequest dhgexreq = new PacketKexDhGexRequest(kxs.dhgexParameters);
tm.sendKexMessage(dhgexreq.getPayload());
}
- kxs.hashAlgo = "SHA1";
+ if (kxs.np.kex_algo.endsWith("sha1")) {
+ kxs.hashAlgo = "SHA1";
+ } else {
+ kxs.hashAlgo = "SHA-256";
+ }
kxs.state = 1;
return;
}
@@ -538,7 +543,8 @@ public class KexManager
if ((kxs == null) || (kxs.state == 0))
throw new IOException("Unexpected Kex submessage!");
- if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1"))
+ if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1")
+ || kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha256"))
{
if (kxs.state == 1)
{
@@ -579,9 +585,10 @@ public class KexManager
try
{
- kxs.H = kxs.dhgx.calculateH(csh.getClientString(), csh.getServerString(),
- kxs.localKEX.getPayload(), kxs.remoteKEX.getPayload(), dhgexrpl.getHostKey(),
- kxs.dhgexParameters);
+ kxs.H = kxs.dhgx.calculateH(kxs.hashAlgo,
+ csh.getClientString(), csh.getServerString(),
+ kxs.localKEX.getPayload(), kxs.remoteKEX.getPayload(),
+ dhgexrpl.getHostKey(), kxs.dhgexParameters);
}
catch (IllegalArgumentException e)
{
diff --git a/src/org/connectbot/ConsoleActivity.java b/src/org/connectbot/ConsoleActivity.java
index 5e34dc6..7aca08c 100644
--- a/src/org/connectbot/ConsoleActivity.java
+++ b/src/org/connectbot/ConsoleActivity.java
@@ -55,6 +55,7 @@ import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.Window;
import android.view.WindowManager;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View.OnClickListener;
@@ -111,6 +112,9 @@ public class ConsoleActivity extends Activity {
private TextView booleanPrompt;
private Button booleanYes, booleanNo;
+ private RelativeLayout keyboardGroup;
+ private Runnable keyboardGroupHider;
+
private TextView empty;
private Animation slide_left_in, slide_left_out, slide_right_in, slide_right_out, fade_stay_hidden, fade_out_delayed;
@@ -133,6 +137,7 @@ public class ConsoleActivity extends Activity {
private ActionBarWrapper actionBar;
private boolean inActionBarMenu = false;
+ private boolean titleBarHide;
private ServiceConnection connection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
@@ -260,6 +265,38 @@ public class ConsoleActivity extends Activity {
booleanPromptGroup.setVisibility(View.GONE);
}
+ private void showEmulatedKeys() {
+ keyboardGroup.startAnimation(keyboard_fade_in);
+ keyboardGroup.setVisibility(View.VISIBLE);
+ actionBar.show();
+
+ if (keyboardGroupHider != null)
+ handler.removeCallbacks(keyboardGroupHider);
+ keyboardGroupHider = new Runnable() {
+ public void run() {
+ if (keyboardGroup.getVisibility() == View.GONE || inActionBarMenu)
+ return;
+
+ keyboardGroup.startAnimation(keyboard_fade_out);
+ keyboardGroup.setVisibility(View.GONE);
+ if (titleBarHide) {
+ actionBar.hide();
+ }
+ keyboardGroupHider = null;
+ }
+ };
+ handler.postDelayed(keyboardGroupHider, KEYBOARD_DISPLAY_TIME);
+ }
+
+ private void hideEmulatedKeys() {
+ if (keyboardGroupHider != null)
+ handler.removeCallbacks(keyboardGroupHider);
+ keyboardGroup.setVisibility(View.GONE);
+ if (titleBarHide) {
+ actionBar.hide();
+ }
+ }
+
// more like configureLaxMode -- enable network IO on UI thread
private void configureStrictMode() {
try {
@@ -275,11 +312,16 @@ public class ConsoleActivity extends Activity {
hardKeyboard = getResources().getConfiguration().keyboard ==
Configuration.KEYBOARD_QWERTY;
- this.setContentView(R.layout.act_console);
-
clipboard = (ClipboardManager)getSystemService(CLIPBOARD_SERVICE);
prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ titleBarHide = prefs.getBoolean(PreferenceConstants.TITLEBARHIDE, false);
+ if (titleBarHide) {
+ getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
+ }
+
+ this.setContentView(R.layout.act_console);
+
// hide status bar if requested by user
if (prefs.getBoolean(PreferenceConstants.FULLSCREEN, false)) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
@@ -358,7 +400,7 @@ public class ConsoleActivity extends Activity {
inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- final RelativeLayout keyboardGroup = (RelativeLayout) findViewById(R.id.keyboard_group);
+ keyboardGroup = (RelativeLayout) findViewById(R.id.keyboard_group);
mKeyboardButton = (ImageView) findViewById(R.id.button_keyboard);
mKeyboardButton.setOnClickListener(new OnClickListener() {
@@ -368,8 +410,7 @@ public class ConsoleActivity extends Activity {
return;
inputManager.showSoftInput(flip, InputMethodManager.SHOW_FORCED);
- keyboardGroup.setVisibility(View.GONE);
- actionBar.hide();
+ hideEmulatedKeys();
}
});
@@ -382,9 +423,7 @@ public class ConsoleActivity extends Activity {
TerminalKeyListener handler = terminal.bridge.getKeyHandler();
handler.metaPress(TerminalKeyListener.OUR_CTRL_ON);
-
- keyboardGroup.setVisibility(View.GONE);
- actionBar.hide();
+ hideEmulatedKeys();
}
});
@@ -397,21 +436,20 @@ public class ConsoleActivity extends Activity {
TerminalKeyListener handler = terminal.bridge.getKeyHandler();
handler.sendEscape();
-
- keyboardGroup.setVisibility(View.GONE);
- actionBar.hide();
+ hideEmulatedKeys();
}
});
actionBar = ActionBarWrapper.getActionBar(this);
actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.hide();
+ if (titleBarHide) {
+ actionBar.hide();
+ }
actionBar.addOnMenuVisibilityListener(new ActionBarWrapper.OnMenuVisibilityListener() {
public void onMenuVisibilityChanged(boolean isVisible) {
inActionBarMenu = isVisible;
if (isVisible == false) {
- keyboardGroup.setVisibility(View.GONE);
- actionBar.hide();
+ hideEmulatedKeys();
}
}
});
@@ -580,20 +618,7 @@ public class ConsoleActivity extends Activity {
&& event.getEventTime() - event.getDownTime() < CLICK_TIME
&& Math.abs(event.getX() - lastX) < MAX_CLICK_DISTANCE
&& Math.abs(event.getY() - lastY) < MAX_CLICK_DISTANCE) {
- keyboardGroup.startAnimation(keyboard_fade_in);
- keyboardGroup.setVisibility(View.VISIBLE);
- actionBar.show();
-
- handler.postDelayed(new Runnable() {
- public void run() {
- if (keyboardGroup.getVisibility() == View.GONE || inActionBarMenu)
- return;
-
- keyboardGroup.startAnimation(keyboard_fade_out);
- keyboardGroup.setVisibility(View.GONE);
- actionBar.hide();
- }
- }, KEYBOARD_DISPLAY_TIME);
+ showEmulatedKeys();
}
// pass any touch events back to detector
diff --git a/src/org/connectbot/GeneratePubkeyActivity.java b/src/org/connectbot/GeneratePubkeyActivity.java
index 3a438c5..1b8995f 100644
--- a/src/org/connectbot/GeneratePubkeyActivity.java
+++ b/src/org/connectbot/GeneratePubkeyActivity.java
@@ -22,6 +22,7 @@ import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
+import java.security.Security;
import org.connectbot.bean.PubkeyBean;
import org.connectbot.util.EntropyDialog;
@@ -44,6 +45,7 @@ import android.view.View.OnFocusChangeListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
+import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.RadioGroup.OnCheckedChangeListener;
import android.widget.SeekBar;
@@ -113,6 +115,11 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere
password1.addTextChangedListener(textChecker);
password2.addTextChangedListener(textChecker);
+ // TODO add BC to provide EC for devices that don't have it.
+ if (Security.getProviders("KeyPairGenerator.EC") == null) {
+ ((RadioButton) findViewById(R.id.ec)).setEnabled(false);
+ }
+
keyTypeGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(RadioGroup group, int checkedId) {
diff --git a/src/org/connectbot/HostListActivity.java b/src/org/connectbot/HostListActivity.java
index 5fa09a9..648b705 100644
--- a/src/org/connectbot/HostListActivity.java
+++ b/src/org/connectbot/HostListActivity.java
@@ -303,7 +303,7 @@ public class HostListActivity extends ListActivity {
keys.setIcon(android.R.drawable.ic_lock_lock);
keys.setIntent(new Intent(HostListActivity.this, PubkeyListActivity.class));
- MenuItem colors = menu.add("Colors");
+ MenuItem colors = menu.add(R.string.title_colors);
colors.setIcon(android.R.drawable.ic_menu_slideshow);
colors.setIntent(new Intent(HostListActivity.this, ColorsActivity.class));
diff --git a/src/org/connectbot/service/KeyEventUtil.java b/src/org/connectbot/service/KeyEventUtil.java
new file mode 100644
index 0000000..1362afa
--- /dev/null
+++ b/src/org/connectbot/service/KeyEventUtil.java
@@ -0,0 +1,149 @@
+/*
+ * ConnectBot: simple, powerful, open-source SSH client for Android
+ * Copyright 2014 Torne Wuff
+ *
+ * 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;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.view.KeyEvent;
+
+public class KeyEventUtil {
+ static final char CONTROL_LIMIT = ' ';
+ static final char PRINTABLE_LIMIT = '\u007e';
+ static final char[] HEX_DIGITS = new char[] {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+ };
+
+ static String printableRepresentation(String source) {
+ if (source == null)
+ return null;
+
+ final StringBuilder sb = new StringBuilder();
+ final int limit = source.length();
+ char[] hexbuf = null;
+ int pointer = 0;
+
+ sb.append('"');
+ while (pointer < limit) {
+ int ch = source.charAt(pointer++);
+ switch (ch) {
+ case '\0':
+ sb.append("\\0");
+ break;
+ case '\t':
+ sb.append("\\t");
+ break;
+ case '\n':
+ sb.append("\\n");
+ break;
+ case '\r':
+ sb.append("\\r");
+ break;
+ case '\"':
+ sb.append("\\\"");
+ break;
+ case '\\':
+ sb.append("\\\\");
+ break;
+ default:
+ if (CONTROL_LIMIT <= ch && ch <= PRINTABLE_LIMIT) {
+ sb.append((char) ch);
+ } else {
+ sb.append("\\u");
+ if (hexbuf == null)
+ hexbuf = new char[4];
+ for (int offs = 4; offs > 0; ) {
+ hexbuf[--offs] = HEX_DIGITS[ch & 0xf];
+ ch >>>= 4;
+ }
+ sb.append(hexbuf, 0, 4);
+ }
+ }
+ }
+ return sb.append('"').toString();
+ }
+
+ private static class ClassCompat {
+ private static final ClassCompat INSTANCE;
+ static {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
+ INSTANCE = new HCMR2AndNewer();
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
+ INSTANCE = new HCMR1AndNewer();
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
+ INSTANCE = new GingerbreadAndNewer();
+ } else {
+ INSTANCE = new ClassCompat();
+ }
+ }
+
+ private ClassCompat() {
+ }
+
+ public static void appendExtras(StringBuilder d, int keyCode, KeyEvent event) {
+ INSTANCE.appendForApi(d, keyCode, event);
+ }
+
+ protected void appendForApi(StringBuilder d, int keyCode, KeyEvent event) {
+ }
+
+ @TargetApi(9)
+ private static class GingerbreadAndNewer extends ClassCompat {
+ @Override
+ protected void appendForApi(StringBuilder d, int keyCode, KeyEvent event) {
+ super.appendForApi(d, keyCode, event);
+ d.append(", source=").append(event.getSource());
+
+ }
+ }
+ @TargetApi(12)
+ private static class HCMR1AndNewer extends GingerbreadAndNewer {
+ protected void appendForApi(StringBuilder d, int keyCode, KeyEvent event) {
+ super.appendForApi(d, keyCode, event);
+ d.append(", keyCodeToString=").append(KeyEvent.keyCodeToString(keyCode));
+ }
+ }
+
+ @TargetApi(13)
+ private static class HCMR2AndNewer extends HCMR1AndNewer {
+ @Override
+ protected void appendForApi(StringBuilder d, int keyCode, KeyEvent event) {
+ super.appendForApi(d, keyCode, event);
+ d.append(", modifiers=").append(Integer.toHexString(event.getModifiers()));
+ }
+ }
+ }
+
+ public static String describeKeyEvent(int keyCode, KeyEvent event) {
+ StringBuilder d = new StringBuilder();
+ d.append("keyCode=").append(keyCode);
+ d.append(", event.toString=").append(event.toString());
+ d.append(", action=").append(event.getAction());
+ d.append(", characters=").append(printableRepresentation(event.getCharacters()));
+ d.append(", deviceId=").append(event.getDeviceId());
+ d.append(", displayLabel=").append((int) event.getDisplayLabel());
+ d.append(", flags=0x").append(Integer.toHexString(event.getFlags()));
+ d.append(", printingKey=").append(event.isPrintingKey());
+ d.append(", keyCode=").append(event.getKeyCode());
+ d.append(", metaState=0x").append(Integer.toHexString(event.getMetaState()));
+ d.append(", number=").append((int) event.getNumber());
+ d.append(", scanCode=").append(event.getScanCode());
+ d.append(", unicodeChar=").append(event.getUnicodeChar());
+ ClassCompat.appendExtras(d, keyCode, event);
+ return d.toString();
+ }
+}
diff --git a/src/org/connectbot/service/TerminalKeyListener.java b/src/org/connectbot/service/TerminalKeyListener.java
index 3f82259..7ff21df 100644
--- a/src/org/connectbot/service/TerminalKeyListener.java
+++ b/src/org/connectbot/service/TerminalKeyListener.java
@@ -64,7 +64,11 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha
// backport constants from api level 11
private final static int KEYCODE_ESCAPE = 111;
- private final static int HC_META_CTRL_ON = 4096;
+ private final static int HC_META_CTRL_ON = 0x1000;
+ private final static int HC_META_CTRL_LEFT_ON = 0x2000;
+ private final static int HC_META_CTRL_RIGHT_ON = 0x4000;
+ private final static int HC_META_CTRL_MASK = HC_META_CTRL_ON | HC_META_CTRL_RIGHT_ON
+ | HC_META_CTRL_LEFT_ON;
private final static int HC_META_ALT_MASK = KeyEvent.META_ALT_ON | KeyEvent.META_ALT_LEFT_ON
| KeyEvent.META_ALT_RIGHT_ON;
@@ -164,6 +168,8 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha
return false;
}
+ //Log.i("CBKeyDebug", KeyEventUtil.describeKeyEvent(keyCode, event));
+
if (volumeKeysChangeFontSize) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
bridge.increaseFontSize();
@@ -283,9 +289,9 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha
// Ask the system to use the keymap to give us the unicode character for this key,
// with our derived modifier state applied.
- int uchar = event.getUnicodeChar(derivedMetaState & ~HC_META_CTRL_ON);
+ int uchar = event.getUnicodeChar(derivedMetaState & ~HC_META_CTRL_MASK);
int ucharWithoutAlt = event.getUnicodeChar(
- derivedMetaState & ~(HC_META_ALT_MASK | HC_META_CTRL_ON));
+ derivedMetaState & ~(HC_META_ALT_MASK | HC_META_CTRL_MASK));
if (uchar != ucharWithoutAlt) {
// The alt key was used to modify the character returned; therefore, drop the alt
// modifier from the state so we don't end up sending alt+key.
diff --git a/src/org/connectbot/util/PreferenceConstants.java b/src/org/connectbot/util/PreferenceConstants.java
index e9fb06c..ad29c39 100644
--- a/src/org/connectbot/util/PreferenceConstants.java
+++ b/src/org/connectbot/util/PreferenceConstants.java
@@ -50,6 +50,7 @@ public class PreferenceConstants {
public static final String ROTATION_AUTOMATIC = "Automatic";
public static final String FULLSCREEN = "fullscreen";
+ public static final String TITLEBARHIDE = "titlebarhide";
public static final String KEYMODE = "keymode";