diff options
Diffstat (limited to 'app/src/main/java')
| -rw-r--r-- | app/src/main/java/de/mud/terminal/vt320.java | 20 | ||||
| -rw-r--r-- | app/src/main/java/org/connectbot/ConsoleActivity.java | 355 | ||||
| -rw-r--r-- | app/src/main/java/org/connectbot/HelpActivity.java | 51 | ||||
| -rw-r--r-- | app/src/main/java/org/connectbot/HintsActivity.java (renamed from app/src/main/java/org/connectbot/HelpTopicActivity.java) | 28 | ||||
| -rw-r--r-- | app/src/main/java/org/connectbot/HostListActivity.java | 1 | ||||
| -rw-r--r-- | app/src/main/java/org/connectbot/PortForwardListActivity.java | 18 | ||||
| -rw-r--r-- | app/src/main/java/org/connectbot/TerminalView.java | 483 | ||||
| -rw-r--r-- | app/src/main/java/org/connectbot/service/TerminalBridge.java | 43 | ||||
| -rw-r--r-- | app/src/main/java/org/connectbot/service/TerminalKeyListener.java | 8 | ||||
| -rw-r--r-- | app/src/main/java/org/connectbot/service/TerminalManager.java | 2 | ||||
| -rw-r--r-- | app/src/main/java/org/connectbot/util/HelpTopicView.java | 62 | ||||
| -rw-r--r-- | app/src/main/java/org/connectbot/util/TerminalViewPager.java | 61 | 
12 files changed, 627 insertions, 505 deletions
| diff --git a/app/src/main/java/de/mud/terminal/vt320.java b/app/src/main/java/de/mud/terminal/vt320.java index dc95bea..3c929b2 100644 --- a/app/src/main/java/de/mud/terminal/vt320.java +++ b/app/src/main/java/de/mud/terminal/vt320.java @@ -672,6 +672,7 @@ public void setScreenSize(int c, int r, boolean broadcast) {    boolean capslock = false;    boolean numlock = false;    int mouserpt = 0; +  int mouserptSaved = 0;    byte mousebut = 0;    boolean useibmcharset = false; @@ -2197,9 +2198,20 @@ public void setScreenSize(int c, int r, boolean broadcast) {              DCEvars[DCEvar] = 0;              term_state = TSTATE_DCEQ;              break; -          case 's': // XTERM_SAVE missing! -            if (true || debug > 1) -              debug("ESC [ ? " + DCEvars[0] + " s unimplemented!"); +          case 's': +            for (int i = 0; i <= DCEvar; i++) { +              switch (DCEvars[i]) { +              case 9: +              case 1000: +              case 1001: +              case 1002: +              case 1003: +                mouserptSaved = mouserpt; +                break; +              default: +                debug("ESC [ ? " + DCEvars[0] + " s, unimplemented!"); +              } +            }              break;            case 'r': // XTERM_RESTORE              if (true || debug > 1) @@ -2227,7 +2239,7 @@ public void setScreenSize(int c, int r, boolean broadcast) {                  case 1001:                  case 1002:                  case 1003: -                  mouserpt = DCEvars[i]; +                  mouserpt = mouserptSaved;                    break;                  default:                    debug("ESC [ ? " + DCEvars[0] + " r, unimplemented!"); diff --git a/app/src/main/java/org/connectbot/ConsoleActivity.java b/app/src/main/java/org/connectbot/ConsoleActivity.java index d628a07..440661a 100644 --- a/app/src/main/java/org/connectbot/ConsoleActivity.java +++ b/app/src/main/java/org/connectbot/ConsoleActivity.java @@ -22,15 +22,14 @@ import java.util.ArrayList;  import java.util.List;  import org.connectbot.bean.HostBean; -import org.connectbot.bean.SelectionArea;  import org.connectbot.service.BridgeDisconnectedListener;  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; +import org.connectbot.util.TerminalViewPager; -import android.annotation.TargetApi;  import android.app.AlertDialog;  import android.app.Dialog;  import android.content.ComponentName; @@ -50,20 +49,15 @@ import android.os.IBinder;  import android.os.Message;  import android.preference.PreferenceManager;  import android.support.annotation.Nullable; -import android.support.v4.app.ActivityCompat;  import android.support.design.widget.TabLayout; +import android.support.v4.app.ActivityCompat;  import android.support.v4.view.MenuItemCompat; -import android.support.v4.view.MotionEventCompat;  import android.support.v4.view.PagerAdapter; -import android.support.v4.view.ViewPager;  import android.support.v7.app.ActionBar;  import android.support.v7.app.AppCompatActivity;  import android.support.v7.widget.Toolbar;  import android.text.ClipboardManager;  import android.util.Log; -import android.view.ContextMenu; -import android.view.GestureDetector; -import android.view.InputDevice;  import android.view.KeyEvent;  import android.view.LayoutInflater;  import android.view.Menu; @@ -74,7 +68,6 @@ import android.view.View;  import android.view.View.OnClickListener;  import android.view.View.OnKeyListener;  import android.view.View.OnTouchListener; -import android.view.ViewConfiguration;  import android.view.ViewGroup;  import android.view.Window;  import android.view.WindowManager; @@ -100,14 +93,12 @@ public class ConsoleActivity extends AppCompatActivity implements BridgeDisconne  	protected static final int REQUEST_EDIT = 1; -	private static final int CLICK_TIME = 400; -	private static final float MAX_CLICK_DISTANCE = 25f;  	private static final int KEYBOARD_DISPLAY_TIME = 3000;  	private static final int KEYBOARD_REPEAT_INITIAL = 500;  	private static final int KEYBOARD_REPEAT = 100;  	private static final String STATE_SELECTED_URI = "selectedUri"; -	protected ViewPager pager = null; +	protected TerminalViewPager pager = null;  	protected TabLayout tabs = null;  	protected Toolbar toolbar = null;  	@Nullable @@ -140,15 +131,11 @@ public class ConsoleActivity extends AppCompatActivity implements BridgeDisconne  	private Animation fade_out_delayed;  	private Animation keyboard_fade_in, keyboard_fade_out; -	private float lastX, lastY;  	private InputMethodManager inputManager;  	private MenuItem disconnect, copy, paste, portForward, resize, urlscan; -	protected TerminalBridge copySource = null; -	private int lastTouchRow, lastTouchCol; -  	private boolean forcedOrientation;  	private Handler handler = new Handler(); @@ -498,10 +485,11 @@ public class ConsoleActivity extends AppCompatActivity implements BridgeDisconne  		inflater = LayoutInflater.from(this);  		toolbar = (Toolbar) findViewById(R.id.toolbar); -		pager = (ViewPager) findViewById(R.id.console_flip); -		registerForContextMenu(pager); + +		pager = (TerminalViewPager) findViewById(R.id.console_flip); +  		pager.addOnPageChangeListener( -				new ViewPager.SimpleOnPageChangeListener() { +				new TerminalViewPager.SimpleOnPageChangeListener() {  					@Override  					public void onPageSelected(int position) {  						setTitle(adapter.getPageTitle(position)); @@ -669,258 +657,17 @@ public class ConsoleActivity extends AppCompatActivity implements BridgeDisconne  		if (tabs != null)  			setupTabLayoutWithViewPager(); -		// detect fling gestures to switch between terminals -		final GestureDetector detect = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() { -			private float totalY = 0; - +		pager.setOnClickListener(new OnClickListener() {  			@Override -			public void onLongPress(MotionEvent e) { -				super.onLongPress(e); -				openContextMenu(pager); -			} - - -			@Override -			public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - -				// if copying, then ignore -				if (copySource != null && copySource.isSelectingForCopy()) -					return false; - -				if (e1 == null || e2 == null) -					return false; - -				// if releasing then reset total scroll -				if (e2.getAction() == MotionEvent.ACTION_UP) { -					totalY = 0; -				} - -				// activate consider if within x tolerance -				int touchSlop = ViewConfiguration.get(ConsoleActivity.this).getScaledTouchSlop(); -				if (Math.abs(e1.getX() - e2.getX()) < touchSlop * 4) { - -					View view = adapter.getCurrentTerminalView(); -					if (view == null) return false; -					TerminalView terminal = (TerminalView) view; - -					// estimate how many rows we have scrolled through -					// accumulate distance that doesn't trigger immediate scroll -					totalY += distanceY; -					final int moved = (int) (totalY / terminal.bridge.charHeight); - -					// consume as scrollback only if towards right half of screen -					if (e2.getX() > view.getWidth() / 2) { -						if (moved != 0) { -							int base = terminal.bridge.buffer.getWindowBase(); -							terminal.bridge.buffer.setWindowBase(base + moved); -							totalY = 0; -							return true; -						} -					} else { -						// otherwise consume as pgup/pgdown for every 5 lines -						if (moved > 5) { -							((vt320) terminal.bridge.buffer).keyPressed(vt320.KEY_PAGE_DOWN, ' ', 0); -							terminal.bridge.tryKeyVibrate(); -							totalY = 0; -							return true; -						} else if (moved < -5) { -							((vt320) terminal.bridge.buffer).keyPressed(vt320.KEY_PAGE_UP, ' ', 0); -							terminal.bridge.tryKeyVibrate(); -							totalY = 0; -							return true; -						} - -					} - -				} - -				return false; -			} - - -		}); - -		pager.setLongClickable(true); -		pager.setOnTouchListener(new OnTouchListener() { - -			public boolean onTouch(View v, MotionEvent event) { -				TerminalBridge bridge = adapter.getCurrentTerminalView().bridge; - -				// Handle mouse-specific actions. -				if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && -						MotionEventCompat.getSource(event) == InputDevice.SOURCE_MOUSE) { -					if (onMouseEvent(event, bridge)) { -						return true; -					} -				} - -				// when copying, highlight the area -				if (copySource != null && copySource.isSelectingForCopy()) { -					SelectionArea area = copySource.getSelectionArea(); -					int row = (int) Math.floor(event.getY() / bridge.charHeight); -					int col = (int) Math.floor(event.getX() / bridge.charWidth); - -					switch (event.getAction()) { -					case MotionEvent.ACTION_DOWN: -						// recording starting area -						if (area.isSelectingOrigin()) { -							area.setRow(row); -							area.setColumn(col); -							lastTouchRow = row; -							lastTouchCol = col; -							copySource.redraw(); -						} -						return true; -					case MotionEvent.ACTION_MOVE: -						/* ignore when user hasn't moved since last time so -						 * we can fine-tune with directional pad -						 */ -						if (row == lastTouchRow && col == lastTouchCol) -							return true; - -						// if the user moves, start the selection for other corner -						area.finishSelectingOrigin(); - -						// update selected area -						area.setRow(row); -						area.setColumn(col); -						lastTouchRow = row; -						lastTouchCol = col; -						copySource.redraw(); -						return true; -					case MotionEvent.ACTION_UP: -						/* If they didn't move their finger, maybe they meant to -						 * select the rest of the text with the directional pad. -						 */ -						if (area.getLeft() == area.getRight() && -								area.getTop() == area.getBottom()) { -							return true; -						} - -						// copy selected area to clipboard -						String copiedText = area.copyFrom(copySource.buffer); - -						clipboard.setText(copiedText); -						Toast.makeText(ConsoleActivity.this, getString(R.string.console_copy_done, copiedText.length()), Toast.LENGTH_LONG).show(); -						// fall through to clear state - -					case MotionEvent.ACTION_CANCEL: -						// make sure we clear any highlighted area -						area.reset(); -						copySource.setSelectingForCopy(false); -						copySource.redraw(); -						return true; -					} -				} - -				if (event.getAction() == MotionEvent.ACTION_DOWN) { -					lastX = event.getX(); -					lastY = event.getY(); -				} else if (event.getAction() == MotionEvent.ACTION_UP -						&& 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) { -					showEmulatedKeys(true); -				} - -				// pass any touch events back to detector -				return detect.onTouchEvent(event); -			} - -			/** -			 * @param event -			 * @param bridge -			 * @return True if the event is handled. -			 */ -			@TargetApi(14) -			private boolean onMouseEvent(MotionEvent event, TerminalBridge bridge) { -				int row = (int) Math.floor(event.getY() / bridge.charHeight); -				int col = (int) Math.floor(event.getX() / bridge.charWidth); -				int meta = event.getMetaState(); -				boolean shiftOn = (meta & KeyEvent.META_SHIFT_ON) != 0; -				boolean mouseReport = ((vt320) bridge.buffer).isMouseReportEnabled(); - -				// MouseReport can be "defeated" using the shift key. -				if ((!mouseReport || shiftOn)) { -					if (event.getAction() == MotionEvent.ACTION_DOWN) { -						switch (event.getButtonState()) { -						case MotionEvent.BUTTON_PRIMARY: -							// Automatically start copy mode if using a mouse. -							startCopyMode(); -							break; -						case MotionEvent.BUTTON_SECONDARY: -							openContextMenu(pager); -							return true; -						case MotionEvent.BUTTON_TERTIARY: -							// Middle click pastes. -							pasteIntoTerminal(); -							return true; -						} -					} -				} else if (event.getAction() == MotionEvent.ACTION_DOWN) { -					((vt320) bridge.buffer).mousePressed( -							col, row, mouseEventToJavaModifiers(event)); -					return true; -				} else if (event.getAction() == MotionEvent.ACTION_UP) { -					((vt320) bridge.buffer).mouseReleased(col, row); -					return true; -				} else if (event.getAction() == MotionEvent.ACTION_MOVE) { -					int buttonState = event.getButtonState(); -					int button = (buttonState & MotionEvent.BUTTON_PRIMARY) != 0 ? 0 : -							(buttonState & MotionEvent.BUTTON_SECONDARY) != 0 ? 1 : -									(buttonState & MotionEvent.BUTTON_TERTIARY) != 0 ? 2 : 3; -					((vt320) bridge.buffer).mouseMoved( -							button, -							col, -							row, -							(meta & KeyEvent.META_CTRL_ON) != 0, -							(meta & KeyEvent.META_SHIFT_ON) != 0, -							(meta & KeyEvent.META_META_ON) != 0); -					return true; +			public void onClick(View v) { +				if (keyboardGroup.getVisibility() == View.GONE) { +					showEmulatedKeys(false);  				} - -				return false;  			} -  		});  	}  	/** -	 * Takes an android mouse event and produces a Java InputEvent modifiers int which can be -	 * passed to vt320. -	 * @param mouseEvent The {@link MotionEvent} which should be a mouse click or release. -	 * @return A Java InputEvent modifier int. See -	 * http://docs.oracle.com/javase/7/docs/api/java/awt/event/InputEvent.html -	 */ -	@TargetApi(14) -	private static int mouseEventToJavaModifiers(MotionEvent mouseEvent) { -		if (MotionEventCompat.getSource(mouseEvent) != InputDevice.SOURCE_MOUSE) return 0; - -		int mods = 0; - -		// See http://docs.oracle.com/javase/7/docs/api/constant-values.html -		int buttonState = mouseEvent.getButtonState(); -		if ((buttonState & MotionEvent.BUTTON_PRIMARY) != 0) -			mods |= 16; -		if ((buttonState & MotionEvent.BUTTON_SECONDARY) != 0) -			mods |= 8; -		if ((buttonState & MotionEvent.BUTTON_TERTIARY) != 0) -			mods |= 4; - -		// Note: Meta and Ctrl are intentionally swapped here to keep logic in vt320 simple. -		int meta = mouseEvent.getMetaState(); -		if ((meta & KeyEvent.META_META_ON) != 0) -			mods |= 2; -		if ((meta & KeyEvent.META_SHIFT_ON) != 0) -			mods |= 1; -		if ((meta & KeyEvent.META_CTRL_ON) != 0) -			mods |= 4; - -		return mods; -	} - -	/**  	 * Ties the {@link TabLayout} to the {@link ViewPager}.  	 *  	 * <p>This method will: @@ -1011,19 +758,21 @@ public class ConsoleActivity extends AppCompatActivity implements BridgeDisconne  			}  		}); -		copy = menu.add(R.string.console_menu_copy); -		if (hardKeyboard) -			copy.setAlphabeticShortcut('c'); -		MenuItemCompat.setShowAsAction(copy, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM); -		copy.setIcon(R.drawable.ic_action_copy); -		copy.setEnabled(activeTerminal); -		copy.setOnMenuItemClickListener(new OnMenuItemClickListener() { -			public boolean onMenuItemClick(MenuItem item) { -				startCopyMode(); -				Toast.makeText(ConsoleActivity.this, getString(R.string.console_copy_start), Toast.LENGTH_LONG).show(); -				return true; -			} -		}); +		if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { +			copy = menu.add(R.string.console_menu_copy); +			if (hardKeyboard) +				copy.setAlphabeticShortcut('c'); +			MenuItemCompat.setShowAsAction(copy, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM); +			copy.setIcon(R.drawable.ic_action_copy); +			copy.setEnabled(activeTerminal); +			copy.setOnMenuItemClickListener(new OnMenuItemClickListener() { +				public boolean onMenuItemClick(MenuItem item) { +					adapter.getCurrentTerminalView().startPreHoneycombCopyMode(); +					Toast.makeText(ConsoleActivity.this, getString(R.string.console_copy_start), Toast.LENGTH_LONG).show(); +					return true; +				} +			}); +		}  		paste = menu.add(R.string.console_menu_paste);  		if (hardKeyboard) @@ -1144,7 +893,10 @@ public class ConsoleActivity extends AppCompatActivity implements BridgeDisconne  			disconnect.setTitle(R.string.list_host_disconnect);  		else  			disconnect.setTitle(R.string.console_menu_close); -		copy.setEnabled(activeTerminal); + +		if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { +			copy.setEnabled(activeTerminal); +		}  		paste.setEnabled(clipboard.hasText() && sessionOpen);  		portForward.setEnabled(sessionOpen && canForwardPorts);  		urlscan.setEnabled(activeTerminal); @@ -1174,32 +926,6 @@ public class ConsoleActivity extends AppCompatActivity implements BridgeDisconne  	}  	@Override -	public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { -		final TerminalView view = adapter.getCurrentTerminalView(); -		boolean activeTerminal = view != null; -		boolean sessionOpen = false; - -		if (activeTerminal) { -			TerminalBridge bridge = view.bridge; -			sessionOpen = bridge.isSessionOpen(); -		} - -		MenuItem paste = menu.add(R.string.console_menu_paste); -		if (hardKeyboard) -			paste.setAlphabeticShortcut('v'); -		paste.setIcon(android.R.drawable.ic_menu_edit); -		paste.setEnabled(clipboard.hasText() && sessionOpen); -		paste.setOnMenuItemClickListener(new OnMenuItemClickListener() { -			public boolean onMenuItemClick(MenuItem item) { -				pasteIntoTerminal(); -				return true; -			} -		}); - - -	} - -	@Override  	public void onStart() {  		super.onStart(); @@ -1308,21 +1034,6 @@ public class ConsoleActivity extends AppCompatActivity implements BridgeDisconne  		super.onSaveInstanceState(savedInstanceState);  	} -	private void startCopyMode() { -		// mark as copying and reset any previous bounds -		TerminalView terminalView = (TerminalView) adapter.getCurrentTerminalView(); -		copySource = terminalView.bridge; - -		SelectionArea area = copySource.getSelectionArea(); -		area.reset(); -		area.setBounds(copySource.buffer.getColumns(), copySource.buffer.getRows()); - -		copySource.setSelectingForCopy(true); - -		// Make sure we show the initial selection -		copySource.redraw(); -	} -  	/**  	 * Save the currently shown {@link TerminalView} as the default. This is  	 * saved back down into {@link TerminalManager} where we can read it again @@ -1494,7 +1205,7 @@ public class ConsoleActivity extends AppCompatActivity implements BridgeDisconne  			overlay.setText(bridge.host.getNickname());  			// and add our terminal view control, using index to place behind overlay -			final TerminalView terminal = new TerminalView(container.getContext(), bridge); +			final TerminalView terminal = new TerminalView(container.getContext(), bridge, pager);  			terminal.setId(R.id.terminal_view);  			view.addView(terminal, 0); @@ -1572,7 +1283,9 @@ public class ConsoleActivity extends AppCompatActivity implements BridgeDisconne  		public TerminalView getCurrentTerminalView() {  			View currentView = pager.findViewWithTag(getBridgeAtPosition(pager.getCurrentItem())); -			if (currentView == null) return null; +			if (currentView == null) { +				return null; +			}  			return (TerminalView) currentView.findViewById(R.id.terminal_view);  		}  	} diff --git a/app/src/main/java/org/connectbot/HelpActivity.java b/app/src/main/java/org/connectbot/HelpActivity.java index 3df8b2f..2a522dc 100644 --- a/app/src/main/java/org/connectbot/HelpActivity.java +++ b/app/src/main/java/org/connectbot/HelpActivity.java @@ -17,30 +17,20 @@  package org.connectbot; -import java.io.IOException; -  import android.app.AlertDialog;  import android.content.Intent; -import android.content.res.AssetManager;  import android.os.Bundle;  import android.support.v7.app.AppCompatActivity; -import android.util.Log;  import android.view.LayoutInflater;  import android.view.View;  import android.view.View.OnClickListener;  import android.widget.Button; -import android.widget.LinearLayout;  /**   * @author Kenny Root   *   */  public class HelpActivity extends AppCompatActivity { -	public final static String TAG = "CB.HelpActivity"; - -	public final static String HELPDIR = "help"; -	public final static String SUFFIX = ".html"; -  	private LayoutInflater inflater = null; @@ -49,54 +39,33 @@ public class HelpActivity extends AppCompatActivity {  		super.onCreate(icicle);  		setContentView(R.layout.act_help); -		AssetManager am = this.getAssets(); -		LinearLayout content = (LinearLayout) findViewById(R.id.topics); - -		try { -			for (String name : am.list(HELPDIR)) { -				if (name.endsWith(SUFFIX)) { -					Button button = new Button(this); -					final String topic = name.substring(0, name.length() - SUFFIX.length()); -					button.setText(topic); - -					button.setOnClickListener(new OnClickListener() { -						public void onClick(View v) { -							Intent intent = new Intent(HelpActivity.this, HelpTopicActivity.class); -							intent.putExtra(Intent.EXTRA_TITLE, topic); -							HelpActivity.this.startActivity(intent); -						} -					}); - -					content.addView(button); -				} +		Button hintsButton = (Button) findViewById(R.id.hints_button); +		hintsButton.setOnClickListener(new OnClickListener() { +			public void onClick(View v) { +				Intent intent = new Intent(HelpActivity.this, HintsActivity.class); +				HelpActivity.this.startActivity(intent);  			} -		} catch (IOException e) { -			// TODO Auto-generated catch block -			Log.e(TAG, "couldn't get list of help assets", e); -		} +		});  		inflater = LayoutInflater.from(this); -		Button shortcutsButton = new Button(this); -		shortcutsButton.setText(getResources().getString(R.string.keyboard_shortcuts)); +		Button shortcutsButton = (Button) findViewById(R.id.shortcuts_button);  		shortcutsButton.setOnClickListener(new OnClickListener() {  			public void onClick(View v) { -				final View shortcuts = inflater.inflate(R.layout.dia_keyboard_shortcuts, null, false); +				final View shortcuts = inflater.inflate(R.layout.dia_keyboard_shortcuts, +						null, false);  				new AlertDialog.Builder(HelpActivity.this)  						.setView(shortcuts)  						.setTitle(R.string.keyboard_shortcuts)  						.show();  			}  		}); -		content.addView(shortcutsButton); -		Button eulaButton = new Button(this); -		eulaButton.setText(getResources().getString(R.string.terms_and_conditions)); +		Button eulaButton = (Button) findViewById(R.id.eula_button);  		eulaButton.setOnClickListener(new OnClickListener() {  			public void onClick(View v) {  				Intent intent = new Intent(HelpActivity.this, EulaActivity.class);  				HelpActivity.this.startActivity(intent);  			}  		}); -		content.addView(eulaButton);  	}  } diff --git a/app/src/main/java/org/connectbot/HelpTopicActivity.java b/app/src/main/java/org/connectbot/HintsActivity.java index 9f5573a..25bc691 100644 --- a/app/src/main/java/org/connectbot/HelpTopicActivity.java +++ b/app/src/main/java/org/connectbot/HintsActivity.java @@ -1,6 +1,6 @@  /*   * ConnectBot: simple, powerful, open-source SSH client for Android - * Copyright 2007 Kenny Root, Jeffrey Sharkey + * Copyright 2015 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. @@ -17,32 +17,14 @@  package org.connectbot; -import org.connectbot.util.HelpTopicView; - -import android.content.Intent;  import android.os.Bundle;  import android.support.v7.app.AppCompatActivity; -/** - * @author Kenny Root - * - */ -public class HelpTopicActivity extends AppCompatActivity { -	public final static String TAG = "CB.HelpActivity"; +public class HintsActivity extends AppCompatActivity {  	@Override -	public void onCreate(Bundle icicle) { -		super.onCreate(icicle); -		setContentView(R.layout.act_help_topic); - -		String topic = getIntent().getStringExtra(Intent.EXTRA_TITLE); - -		this.setTitle(String.format("%s: %s", -				getResources().getText(R.string.title_help), -				topic)); - -		HelpTopicView helpTopic = (HelpTopicView) findViewById(R.id.topic_text); - -		helpTopic.setTopic(topic); +	protected void onCreate(Bundle savedInstanceState) { +		super.onCreate(savedInstanceState); +		setContentView(R.layout.act_hints);  	}  } diff --git a/app/src/main/java/org/connectbot/HostListActivity.java b/app/src/main/java/org/connectbot/HostListActivity.java index cb53bf0..509ef80 100644 --- a/app/src/main/java/org/connectbot/HostListActivity.java +++ b/app/src/main/java/org/connectbot/HostListActivity.java @@ -173,6 +173,7 @@ public class HostListActivity extends AppCompatListActivity implements OnHostSta  	public void onCreate(Bundle icicle) {  		super.onCreate(icicle);  		setContentView(R.layout.act_hostlist); +		setTitle(R.string.title_hosts_list);  		mListView = (RecyclerView) findViewById(R.id.list);  		mListView.setHasFixedSize(true); diff --git a/app/src/main/java/org/connectbot/PortForwardListActivity.java b/app/src/main/java/org/connectbot/PortForwardListActivity.java index c7f0e7c..e5d3210 100644 --- a/app/src/main/java/org/connectbot/PortForwardListActivity.java +++ b/app/src/main/java/org/connectbot/PortForwardListActivity.java @@ -188,11 +188,23 @@ public class PortForwardListActivity extends AppCompatListActivity {  										break;  									} +									// Why length(), not isEmpty(), is used: http://stackoverflow.com/q/10606725 +									String sourcePort = sourcePortEdit.getText().toString(); +									if (sourcePort.length() == 0) { +										sourcePort = sourcePortEdit.getHint().toString(); +									} + +									String destination = destEdit.getText().toString(); +									if (destination.length() == 0) { +										destination = destEdit.getHint().toString(); +									} +  									PortForwardBean portForward = new PortForwardBean(  											host != null ? host.getId() : -1, -											nicknameEdit.getText().toString(), type, -											sourcePortEdit.getText().toString(), -											destEdit.getText().toString()); +											nicknameEdit.getText().toString(), +											type, +											sourcePort, +											destination);  									if (hostBridge != null) {  										hostBridge.addPortForward(portForward); diff --git a/app/src/main/java/org/connectbot/TerminalView.java b/app/src/main/java/org/connectbot/TerminalView.java index 7c4f51f..bc095fc 100644 --- a/app/src/main/java/org/connectbot/TerminalView.java +++ b/app/src/main/java/org/connectbot/TerminalView.java @@ -25,47 +25,72 @@ import org.connectbot.bean.SelectionArea;  import org.connectbot.service.FontSizeChangedListener;  import org.connectbot.service.TerminalBridge;  import org.connectbot.service.TerminalKeyListener; +import org.connectbot.util.TerminalViewPager;  import android.annotation.TargetApi; +import android.app.Activity;  import android.content.ContentResolver;  import android.content.Context;  import android.content.Intent;  import android.content.pm.ResolveInfo;  import android.database.Cursor;  import android.graphics.Canvas; +import android.graphics.Color;  import android.graphics.Matrix;  import android.graphics.Paint;  import android.graphics.Path;  import android.graphics.PixelXorXfermode;  import android.graphics.RectF; +import android.graphics.Typeface;  import android.net.Uri;  import android.os.AsyncTask; +import android.os.Build;  import android.support.v4.view.MotionEventCompat; +import android.text.ClipboardManager; +import android.view.ActionMode; +import android.view.GestureDetector;  import android.view.InputDevice;  import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem;  import android.view.MotionEvent; -import android.view.View;  import android.view.ViewGroup.LayoutParams;  import android.view.accessibility.AccessibilityEvent;  import android.view.accessibility.AccessibilityManager;  import android.view.inputmethod.BaseInputConnection;  import android.view.inputmethod.EditorInfo;  import android.view.inputmethod.InputConnection; +import android.widget.TextView;  import android.widget.Toast;  import de.mud.terminal.VDUBuffer;  import de.mud.terminal.vt320;  /** - * User interface {@link View} for showing a TerminalBridge in an + * User interface {@link TextView} for showing a TerminalBridge in an   * {@link android.app.Activity}. Handles drawing bitmap updates and passing keystrokes down   * to terminal.   * + * On Honeycomb devices and above (>= APIv11), a TextView with transparent text (which is identical + * to the bitmap) is drawn above the bitmap. This TextView exists to allow the user to + * select and copy text. + *   * @author jsharkey   */ -public class TerminalView extends View implements FontSizeChangedListener { +public class TerminalView extends TextView implements FontSizeChangedListener {  	private final Context context;  	public final TerminalBridge bridge; + +	private final TerminalViewPager viewPager; +	private GestureDetector gestureDetector; + +	private ClipboardManager clipboard; +	private ActionMode selectionActionMode = null; +	private String currentSelection = ""; + +	// These are only used for pre-Honeycomb copying. +	private int lastTouchedRow, lastTouchedCol; +  	private final Paint paint;  	private final Paint cursorPaint;  	private final Paint cursorStrokePaint; @@ -96,17 +121,19 @@ public class TerminalView extends View implements FontSizeChangedListener {  	private static final String SCREENREADER_INTENT_ACTION = "android.accessibilityservice.AccessibilityService";  	private static final String SCREENREADER_INTENT_CATEGORY = "android.accessibilityservice.category.FEEDBACK_SPOKEN"; -	public TerminalView(Context context, TerminalBridge bridge) { +	public TerminalView(Context context, TerminalBridge bridge, TerminalViewPager pager) {  		super(context);  		this.context = context;  		this.bridge = bridge; -		paint = new Paint(); +		this.viewPager = pager;  		setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));  		setFocusable(true);  		setFocusableInTouchMode(true); +		paint = new Paint(); +  		cursorPaint = new Paint();  		cursorPaint.setColor(bridge.color[bridge.defaultFg]);  		cursorPaint.setXfermode(new PixelXorXfermode(bridge.color[bridge.defaultBg])); @@ -142,6 +169,7 @@ public class TerminalView extends View implements FontSizeChangedListener {  		scaleMatrix = new Matrix();  		bridge.addFontSizeChangedListener(this); +		bridge.parentChanged(this);  		// connect our view up to the bridge  		setOnKeyListener(bridge.getKeyHandler()); @@ -150,6 +178,400 @@ public class TerminalView extends View implements FontSizeChangedListener {  		// Enable accessibility features if a screen reader is active.  		new AccessibilityStateTester().execute((Void) null); + +		clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + +		setTextColor(Color.TRANSPARENT); +		setTypeface(Typeface.MONOSPACE); +		onFontSizeChanged(bridge.getFontSize()); + +		// Allow selection of and copying text for Honeycomb and above devices. +		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { +			setTextIsSelectable(true); +			setCustomSelectionActionModeCallback(new TextSelectionActionModeCallback()); +		} + +		gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { +			private TerminalBridge bridge = TerminalView.this.bridge; +			private float totalY = 0; + +			@Override +			public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { +				// if releasing then reset total scroll +				if (e2.getAction() == MotionEvent.ACTION_UP) { +					totalY = 0; +				} + +				totalY += distanceY; +				final int moved = (int) (totalY / bridge.charHeight); + +				if (moved != 0) { +					int base = bridge.buffer.getWindowBase(); +					bridge.buffer.setWindowBase(base + moved); +					totalY = 0; +				} + +				return true; +			} + +			@Override +			public boolean onSingleTapConfirmed(MotionEvent e) { +				viewPager.performClick(); +				return super.onSingleTapConfirmed(e); +			} +		}); +	} + +	@TargetApi(11) +	private void closeSelectionActionMode() { +		if (selectionActionMode != null) { +			selectionActionMode.finish(); +			selectionActionMode = null; +		} +	} + +	public void copyCurrentSelectionToClipboard() { +		ClipboardManager clipboard = +				(ClipboardManager) TerminalView.this.context.getSystemService(Context.CLIPBOARD_SERVICE); +		if (currentSelection.length() != 0) { +			clipboard.setText(currentSelection); +		} + +		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { +			closeSelectionActionMode(); +		} +	} + +	@Override +	protected void onSelectionChanged(int selStart, int selEnd) { +		if (selStart <= selEnd) { +			currentSelection = getText().toString().substring(selStart, selEnd); +		} +		super.onSelectionChanged(selStart, selEnd); +	} + +	@Override +	public boolean onTouchEvent(MotionEvent event) { +		if (event.getAction() == MotionEvent.ACTION_DOWN) { +			// Selection may be beginning. Sync the TextView with the buffer. +			refreshTextFromBuffer(); +		} + +		// Mouse input is treated differently: +		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && +				MotionEventCompat.getSource(event) == InputDevice.SOURCE_MOUSE) { +			if (onMouseEvent(event, bridge)) { +				return true; +			} +			viewPager.setPagingEnabled(true); +		} else if (gestureDetector != null) { +			// The gesture detector should not be called if touch event was from mouse. +			gestureDetector.onTouchEvent(event); +		} + +		// Old version of copying, only for pre-Honeycomb. +		if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { +			// when copying, highlight the area +			if (bridge.isSelectingForCopy()) { +				SelectionArea area = bridge.getSelectionArea(); +				int row = (int) Math.floor(event.getY() / bridge.charHeight); +				int col = (int) Math.floor(event.getX() / bridge.charWidth); + +				switch (event.getAction()) { +				case MotionEvent.ACTION_DOWN: +					// recording starting area +					viewPager.setPagingEnabled(false); +					if (area.isSelectingOrigin()) { +						area.setRow(row); +						area.setColumn(col); +						lastTouchedRow = row; +						lastTouchedCol = col; +						bridge.redraw(); +					} +					return true; +				case MotionEvent.ACTION_MOVE: +							/* ignore when user hasn't moved since last time so +							 * we can fine-tune with directional pad +							 */ +					if (row == lastTouchedRow && col == lastTouchedCol) +						return true; + +					// if the user moves, start the selection for other corner +					area.finishSelectingOrigin(); + +					// update selected area +					area.setRow(row); +					area.setColumn(col); +					lastTouchedRow = row; +					lastTouchedCol = col; +					bridge.redraw(); +					return true; +				case MotionEvent.ACTION_UP: +							/* If they didn't move their finger, maybe they meant to +							 * select the rest of the text with the directional pad. +							 */ +					if (area.getLeft() == area.getRight() && +							area.getTop() == area.getBottom()) { +						return true; +					} + +					// copy selected area to clipboard +					String copiedText = area.copyFrom(bridge.buffer); + +					clipboard.setText(copiedText); +					Toast.makeText( +						context, +						context.getString(R.string.console_copy_done, copiedText.length()), +						Toast.LENGTH_LONG).show(); + +					// fall through to clear state + +				case MotionEvent.ACTION_CANCEL: +					// make sure we clear any highlighted area +					area.reset(); +					bridge.setSelectingForCopy(false); +					bridge.redraw(); +					viewPager.setPagingEnabled(true); +					return true; +				} +			} + +			return true; +		} + +		super.onTouchEvent(event); + +		return true; +	} + +	@TargetApi(11) +	private class TextSelectionActionModeCallback implements ActionMode.Callback { +		private static final int COPY = 0; +		private static final int PASTE = 1; + +		@Override +		public boolean onPrepareActionMode(ActionMode mode, Menu menu) { +			return false; +		} + +		@Override +		public boolean onCreateActionMode(ActionMode mode, Menu menu) { +			TerminalView.this.selectionActionMode = mode; + +			menu.clear(); + +			menu.add(0, COPY, 0, R.string.console_menu_copy) +					.setIcon(R.drawable.ic_action_copy) +					.setShowAsAction(MenuItem.SHOW_AS_ACTION_WITH_TEXT | MenuItem.SHOW_AS_ACTION_IF_ROOM); +			menu.add(0, PASTE, 1, R.string.console_menu_paste) +					.setIcon(R.drawable.ic_action_paste) +					.setShowAsAction(MenuItem.SHOW_AS_ACTION_WITH_TEXT | MenuItem.SHOW_AS_ACTION_IF_ROOM); + +			return true; +		} + +		@Override +		public boolean onActionItemClicked(ActionMode mode, MenuItem item) { +			switch (item.getItemId()) { +			case COPY: +				copyCurrentSelectionToClipboard(); +				return true; +			case PASTE: +				String clip = clipboard.getText().toString(); +				TerminalView.this.bridge.injectString(clip); +				mode.finish(); +				return true; +			} + +			return false; +		} + +		@Override +		public void onDestroyActionMode(ActionMode mode) { +		} +	} + +	/** +	 * @param event +	 * @param bridge +	 * @return True if the event is handled. +	 */ +	@TargetApi(14) +	private boolean onMouseEvent(MotionEvent event, TerminalBridge bridge) { +		int row = (int) Math.floor(event.getY() / bridge.charHeight); +		int col = (int) Math.floor(event.getX() / bridge.charWidth); +		int meta = event.getMetaState(); +		boolean shiftOn = (meta & KeyEvent.META_SHIFT_ON) != 0; +		vt320 vtBuffer = (vt320) bridge.buffer; +		boolean mouseReport = vtBuffer.isMouseReportEnabled(); + +		// MouseReport can be "defeated" using the shift key. +		if (!mouseReport || shiftOn) { +			if (event.getAction() == MotionEvent.ACTION_DOWN) { +				if (event.getButtonState() == MotionEvent.BUTTON_TERTIARY) { +					// Middle click pastes. +					String clip = clipboard.getText().toString(); +					bridge.injectString(clip); +					return true; +				} + +				// Begin "selection mode" + +				if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { +					closeSelectionActionMode(); +				} +			} else if (event.getAction() == MotionEvent.ACTION_MOVE) { +				// In the middle of selection. + +				if (selectionActionMode == null) { +					selectionActionMode = startActionMode(new TextSelectionActionModeCallback()); +				} + +				int selectionStart = getSelectionStart(); +				int selectionEnd = getSelectionEnd(); + +				if (selectionStart > selectionEnd) { +					int tempStart = selectionStart; +					selectionStart = selectionEnd; +					selectionEnd = tempStart; +				} + +				currentSelection = getText().toString().substring(selectionStart, selectionEnd); +			} +		} else if (event.getAction() == MotionEvent.ACTION_DOWN) { +			viewPager.setPagingEnabled(false); +			vtBuffer.mousePressed( +					col, row, mouseEventToJavaModifiers(event)); +			return true; +		} else if (event.getAction() == MotionEvent.ACTION_UP) { +			viewPager.setPagingEnabled(true); +			vtBuffer.mouseReleased(col, row); +			return true; +		} else if (event.getAction() == MotionEvent.ACTION_MOVE) { +			int buttonState = event.getButtonState(); +			int button = (buttonState & MotionEvent.BUTTON_PRIMARY) != 0 ? 0 : +				(buttonState & MotionEvent.BUTTON_SECONDARY) != 0 ? 1 : +				(buttonState & MotionEvent.BUTTON_TERTIARY) != 0 ? 2 : 3; +			vtBuffer.mouseMoved( +				button, +				col, +				row, +				(meta & KeyEvent.META_CTRL_ON) != 0, +				(meta & KeyEvent.META_SHIFT_ON) != 0, +				(meta & KeyEvent.META_META_ON) != 0); +			return true; +		} + +		return false; +	} + +	/** +	 * Takes an android mouse event and produces a Java InputEvent modifiers int which can be +	 * passed to vt320. +	 * @param mouseEvent The {@link MotionEvent} which should be a mouse click or release. +	 * @return A Java InputEvent modifier int. See +	 * http://docs.oracle.com/javase/7/docs/api/java/awt/event/InputEvent.html +	 */ +	@TargetApi(14) +	private static int mouseEventToJavaModifiers(MotionEvent mouseEvent) { +		if (MotionEventCompat.getSource(mouseEvent) != InputDevice.SOURCE_MOUSE) return 0; + +		int mods = 0; + +		// See http://docs.oracle.com/javase/7/docs/api/constant-values.html +		int buttonState = mouseEvent.getButtonState(); +		if ((buttonState & MotionEvent.BUTTON_PRIMARY) != 0) +			mods |= 16; +		if ((buttonState & MotionEvent.BUTTON_SECONDARY) != 0) +			mods |= 8; +		if ((buttonState & MotionEvent.BUTTON_TERTIARY) != 0) +			mods |= 4; + +		// Note: Meta and Ctrl are intentionally swapped here to keep logic in vt320 simple. +		int meta = mouseEvent.getMetaState(); +		if ((meta & KeyEvent.META_META_ON) != 0) +			mods |= 2; +		if ((meta & KeyEvent.META_SHIFT_ON) != 0) +			mods |= 1; +		if ((meta & KeyEvent.META_CTRL_ON) != 0) +			mods |= 4; + +		return mods; +	} + +	@Override +	@TargetApi(12) +	public boolean onGenericMotionEvent(MotionEvent event) { +		if ((MotionEventCompat.getSource(event) & InputDevice.SOURCE_CLASS_POINTER) != 0) { +			switch (event.getAction()) { +			case MotionEvent.ACTION_SCROLL: +				// Process scroll wheel movement: +				float yDistance = MotionEventCompat.getAxisValue(event, MotionEvent.AXIS_VSCROLL); +				vt320 vtBuffer = (vt320) bridge.buffer; +				boolean mouseReport = vtBuffer.isMouseReportEnabled(); +				if (mouseReport) { +					int row = (int) Math.floor(event.getY() / bridge.charHeight); +					int col = (int) Math.floor(event.getX() / bridge.charWidth); + +					vtBuffer.mouseWheel( +							yDistance > 0, +							col, +							row, +							(event.getMetaState() & KeyEvent.META_CTRL_ON) != 0, +							(event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0, +							(event.getMetaState() & KeyEvent.META_META_ON) != 0); +					return true; +				} else if (yDistance != 0) { +					int base = bridge.buffer.getWindowBase(); +					bridge.buffer.setWindowBase(base - Math.round(yDistance)); +					return true; +				} +			} +		} +		return super.onGenericMotionEvent(event); +	} + +	// TODO: cleanup and possibly optimize +	private void refreshTextFromBuffer() { +		if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { +			// Do not run this function because the textView is not selectable pre-Honeycomb. +			return; +		} + +		VDUBuffer vb = bridge.getVDUBuffer(); + +		String line = ""; +		String buffer = ""; + +		int windowBase = vb.getWindowBase(); +		int rowBegin = vb.getTopMargin(); +		int rowEnd = vb.getBottomMargin(); +		int numCols = vb.getColumns() - 1; + +		for (int r = rowBegin; r <= rowEnd; r++) { +			for (int c = 0; c < numCols; c++) { +				line += vb.charArray[windowBase + r][c]; +			} +			buffer += line.replaceAll("\\s+$", "") + "\n"; +			line = ""; +		} + +		setText(buffer); +	} + +	/** +	 * Only intended for pre-Honeycomb devices. +	 */ +	public void startPreHoneycombCopyMode() { +		// mark as copying and reset any previous bounds +		SelectionArea area = bridge.getSelectionArea(); +		area.reset(); +		area.setBounds(bridge.buffer.getColumns(), bridge.buffer.getRows()); + +		bridge.setSelectingForCopy(true); + +		// Make sure we show the initial selection +		bridge.redraw();  	}  	public void destroy() { @@ -166,8 +588,21 @@ public class TerminalView extends View implements FontSizeChangedListener {  		scaleCursors();  	} -	public void onFontSizeChanged(float size) { +	public void onFontSizeChanged(final float size) {  		scaleCursors(); + +		((Activity) context).runOnUiThread(new Runnable() { +			@Override +			public void run() { +				setTextSize(size); + +				// For the TextView to line up with the bitmap text, lineHeight must be equal to +				// the bridge's charHeight. See TextView.getLineHeight(), which has been reversed to +				// derive lineSpacingMultiplier. +				float lineSpacingMultiplier = (float) bridge.charHeight / getPaint().getFontMetricsInt(null); +				setLineSpacing(0.0f, lineSpacingMultiplier); +			} +		});  	}  	private void scaleCursors() { @@ -246,7 +681,8 @@ public class TerminalView extends View implements FontSizeChangedListener {  			}  			// draw any highlighted area -			if (bridge.isSelectingForCopy()) { +			if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB && +				bridge.isSelectingForCopy()) {  				SelectionArea area = bridge.getSelectionArea();  				canvas.save(Canvas.CLIP_SAVE_FLAG);  				canvas.clipRect( @@ -259,6 +695,8 @@ public class TerminalView extends View implements FontSizeChangedListener {  				canvas.restore();  			}  		} + +		super.onDraw(canvas);  	}  	public void notifyUser(String message) { @@ -324,37 +762,6 @@ public class TerminalView extends View implements FontSizeChangedListener {  		};  	} -	@Override -	@TargetApi(12) -	public boolean onGenericMotionEvent(MotionEvent event) { -		if ((MotionEventCompat.getSource(event) & InputDevice.SOURCE_CLASS_POINTER) != 0) { -			switch (event.getAction()) { -			case MotionEvent.ACTION_SCROLL: -				// Process scroll wheel movement: -				float yDistance = MotionEventCompat.getAxisValue(event, MotionEvent.AXIS_VSCROLL); -				boolean mouseReport = ((vt320) bridge.buffer).isMouseReportEnabled(); -				if (mouseReport) { -					int row = (int) Math.floor(event.getY() / bridge.charHeight); -					int col = (int) Math.floor(event.getX() / bridge.charWidth); - -					((vt320) bridge.buffer).mouseWheel( -							yDistance > 0, -							col, -							row, -							(event.getMetaState() & KeyEvent.META_CTRL_ON) != 0, -							(event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0, -							(event.getMetaState() & KeyEvent.META_META_ON) != 0); -					return true; -				} else if (yDistance != 0) { -					int base = bridge.buffer.getWindowBase(); -					bridge.buffer.setWindowBase(base - Math.round(yDistance)); -					return true; -				} -			} -		} -		return super.onGenericMotionEvent(event); -	} -  	public void propagateConsoleText(char[] rawText, int length) {  		if (mAccessibilityActive) {  			synchronized (mAccessibilityLock) { diff --git a/app/src/main/java/org/connectbot/service/TerminalBridge.java b/app/src/main/java/org/connectbot/service/TerminalBridge.java index b9e29e8..a888cc3 100644 --- a/app/src/main/java/org/connectbot/service/TerminalBridge.java +++ b/app/src/main/java/org/connectbot/service/TerminalBridge.java @@ -341,6 +341,33 @@ public class TerminalBridge implements VDUDisplay {  	}  	/** +	 * Only intended for pre-Honeycomb devices. +	 */ +	public void setSelectingForCopy(boolean selectingForCopy) { +		this.selectingForCopy = selectingForCopy; +	} + +	/** +	 * Only intended for pre-Honeycomb devices. +	 */ +	public boolean isSelectingForCopy() { +		return selectingForCopy; +	} + +	/** +	 * Only intended for pre-Honeycomb devices. +	 */ +	public SelectionArea getSelectionArea() { +		return selectionArea; +	} + +	public void copyCurrentSelection() { +		if (parent != null) { +			parent.copyCurrentSelectionToClipboard(); +		} +	} + +	/**  	 * Inject a specific string into this terminal. Used for post-login strings  	 * and pasting clipboard.  	 */ @@ -482,18 +509,6 @@ public class TerminalBridge implements VDUDisplay {  		}  	} -	public void setSelectingForCopy(boolean selectingForCopy) { -		this.selectingForCopy = selectingForCopy; -	} - -	public boolean isSelectingForCopy() { -		return selectingForCopy; -	} - -	public SelectionArea getSelectionArea() { -		return selectionArea; -	} -  	public synchronized void tryKeyVibrate() {  		manager.tryKeyVibrate();  	} @@ -538,6 +553,10 @@ public class TerminalBridge implements VDUDisplay {  		forcedSize = false;  	} +	public float getFontSize() { +		return fontSizeDp; +	} +  	/**  	 * Add an {@link FontSizeChangedListener} to the list of listeners for this  	 * bridge. diff --git a/app/src/main/java/org/connectbot/service/TerminalKeyListener.java b/app/src/main/java/org/connectbot/service/TerminalKeyListener.java index 1b2ffe4..753fa86 100644 --- a/app/src/main/java/org/connectbot/service/TerminalKeyListener.java +++ b/app/src/main/java/org/connectbot/service/TerminalKeyListener.java @@ -299,6 +299,14 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha  					return true;  			} +			// CTRL-SHIFT-C to copy. +			if (keyCode == KeyEvent.KEYCODE_C +					&& (derivedMetaState & HC_META_CTRL_ON) != 0 +					&& (derivedMetaState & KeyEvent.META_SHIFT_ON) != 0) { +				bridge.copyCurrentSelection(); +				return true; +			} +  			// CTRL-SHIFT-V to paste.  			if (keyCode == KeyEvent.KEYCODE_V  					&& (derivedMetaState & HC_META_CTRL_ON) != 0 diff --git a/app/src/main/java/org/connectbot/service/TerminalManager.java b/app/src/main/java/org/connectbot/service/TerminalManager.java index 88c0811..e716094 100644 --- a/app/src/main/java/org/connectbot/service/TerminalManager.java +++ b/app/src/main/java/org/connectbot/service/TerminalManager.java @@ -257,7 +257,7 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen  	}  	public String getEmulation() { -		return prefs.getString(PreferenceConstants.EMULATION, "screen"); +		return prefs.getString(PreferenceConstants.EMULATION, "xterm-256color");  	}  	public int getScrollback() { diff --git a/app/src/main/java/org/connectbot/util/HelpTopicView.java b/app/src/main/java/org/connectbot/util/HelpTopicView.java deleted file mode 100644 index 0cbc267..0000000 --- a/app/src/main/java/org/connectbot/util/HelpTopicView.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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.util; - -import org.connectbot.HelpActivity; - -import android.content.Context; -import android.util.AttributeSet; -import android.webkit.WebSettings; -import android.webkit.WebView; - -/** - * @author Kenny Root - * - */ -public class HelpTopicView extends WebView { -	public HelpTopicView(Context context, AttributeSet attrs, int defStyle) { -		super(context, attrs, defStyle); -		initialize(); -	} - -	public HelpTopicView(Context context, AttributeSet attrs) { -		super(context, attrs); -		initialize(); -	} - -	public HelpTopicView(Context context) { -		super(context); -		initialize(); -	} - -	private void initialize() { -		WebSettings wSet = getSettings(); -		wSet.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS); -		wSet.setUseWideViewPort(false); -	} - -	public HelpTopicView setTopic(String topic) { -		String path = String.format("file:///android_asset/%s/%s%s", -				HelpActivity.HELPDIR, topic, HelpActivity.SUFFIX); -		loadUrl(path); - -		computeScroll(); - -		return this; -	} -} diff --git a/app/src/main/java/org/connectbot/util/TerminalViewPager.java b/app/src/main/java/org/connectbot/util/TerminalViewPager.java new file mode 100644 index 0000000..bb06b69 --- /dev/null +++ b/app/src/main/java/org/connectbot/util/TerminalViewPager.java @@ -0,0 +1,61 @@ +/* + * ConnectBot: simple, powerful, open-source SSH client for Android + * Copyright 2015 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.util; + +import android.content.Context; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.view.MotionEvent; + +/** + * Custom ViewPager {@link ViewPager} which is used to swipe between TerminalViews + * {@link org.connectbot.TerminalView}. Also allows temporary disabling of paging + * functionality to prevent event intercepts. + * + * @author rhansby + */ +public class TerminalViewPager extends ViewPager { +	private boolean enabled; + +	public TerminalViewPager(Context context, AttributeSet attrs) { +		super(context, attrs); +		this.enabled = true; +	} + +	@Override +	public boolean onTouchEvent(MotionEvent event) { +		if (this.enabled) { +			return super.onTouchEvent(event); +		} + +		return false; +	} + +	@Override +	public boolean onInterceptTouchEvent(MotionEvent event) { +		if (this.enabled) { +			return super.onInterceptTouchEvent(event); +		} + +		return false; +	} + +	public void setPagingEnabled(boolean enabled) { +		this.enabled = enabled; +	} +} | 
