diff options
Diffstat (limited to 'app/src')
30 files changed, 1163 insertions, 588 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e863b21..dd9db1f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -44,9 +44,9 @@ <activity android:name=".HostListActivity" - android:label="@string/title_hosts_list" + android:label="@string/app_name" android:launchMode="singleTop"> - <intent-filter android:label="@string/app_name"> + <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> @@ -134,29 +134,28 @@ </activity> <activity - android:name=".HelpTopicActivity" + android:name=".ColorsActivity" android:configChanges="keyboardHidden|orientation" - android:label="@string/title_help"> + android:label="@string/title_colors"> <meta-data android:name="android.support.PARENT_ACTIVITY" - android:value="org.connectbot.HelpActivity"/> + android:value="org.connectbot.HostListActivity"/> </activity> <activity - android:name=".ColorsActivity" + android:name=".EulaActivity" android:configChanges="keyboardHidden|orientation" - android:label="@string/title_colors"> + android:label="@string/terms_and_conditions"> <meta-data android:name="android.support.PARENT_ACTIVITY" - android:value="org.connectbot.HostListActivity"/> + android:value="org.connectbot.HelpActivity"/> </activity> <activity - android:name=".EulaActivity" - android:configChanges="keyboardHidden|orientation" - android:label="@string/terms_and_conditions"> + android:name=".HintsActivity" + android:label="@string/hints"> <meta-data android:name="android.support.PARENT_ACTIVITY" diff --git a/app/src/main/assets/help/Hints.html b/app/src/main/assets/help/Hints.html deleted file mode 100644 index 4e07792..0000000 --- a/app/src/main/assets/help/Hints.html +++ /dev/null @@ -1,26 +0,0 @@ -<html> -<body style="background-color: #000; color: #fff"> - -<h2><a name="Shortcuts" />Host Shortcuts</h2> -<p>Long-press on your Android desktop to create direct shortcuts to frequently-used SSH hosts.</p> - -<h2><a name="Page_Up_/_Page_Down" />Page Up / Page Down</h2> -<p>Swiping your finger up and down on the left third of the screen - will send a page up and page down key to the remote host. Many programs - map this to scrolling back into history such as irssi or tinyfugue.</p> -<p><img src="http://connectbot.googlecode.com/svn/trunk/www/gesture-pgup.png" />Page Up gesture</p> -<p><img src="http://connectbot.googlecode.com/svn/trunk/www/gesture-pgdn.png" />Page Down gesture</p> - -<h2><a name="Scroll_back_/_Scroll_forward" />Scroll back / Scroll forward</h2> -<p>Swiping your finger up on the right side of the screen allows you to scroll backward and forward - in the local terminal buffer history.</p> -<p><img src="http://connectbot.googlecode.com/svn/trunk/www/gesture-scrollback.png" />Scroll back gesture</p> -<p><img src="http://connectbot.googlecode.com/svn/trunk/www/gesture-scrollforward.png" />Scroll forward gesture</p> - -<h2><a name="Switching_hosts" />Switching hosts</h2> -<p>Swiping your finger from one side of the screen to the other will switch between currently connected hosts.</p> -<p><img src="http://connectbot.googlecode.com/svn/trunk/www/gesture-hostprev.png" />Previous host gesture</p> -<p><img src="http://connectbot.googlecode.com/svn/trunk/www/gesture-hostnext.png" />Next host gesture</p> - -</body> -</html>
\ No newline at end of file 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/HelpTopicActivity.java deleted file mode 100644 index 9f5573a..0000000 --- a/app/src/main/java/org/connectbot/HelpTopicActivity.java +++ /dev/null @@ -1,48 +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; - -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"; - - @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); - } -} diff --git a/app/src/main/res/layout/act_help_topic.xml b/app/src/main/java/org/connectbot/HintsActivity.java index f02e35e..25bc691 100644 --- a/app/src/main/res/layout/act_help_topic.xml +++ b/app/src/main/java/org/connectbot/HintsActivity.java @@ -1,8 +1,6 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- /* * 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. @@ -16,18 +14,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ ---> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:orientation="vertical" - > +package org.connectbot; - <org.connectbot.util.HelpTopicView - android:id="@+id/topic_text" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - /> +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; -</LinearLayout> +public class HintsActivity extends AppCompatActivity { + + @Override + 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; + } +} diff --git a/app/src/main/res/drawable-mdpi/gesture_hostnext.png b/app/src/main/res/drawable-mdpi/gesture_hostnext.png Binary files differnew file mode 100644 index 0000000..624223d --- /dev/null +++ b/app/src/main/res/drawable-mdpi/gesture_hostnext.png diff --git a/app/src/main/res/drawable-mdpi/gesture_hostprev.png b/app/src/main/res/drawable-mdpi/gesture_hostprev.png Binary files differnew file mode 100644 index 0000000..f99f70c --- /dev/null +++ b/app/src/main/res/drawable-mdpi/gesture_hostprev.png diff --git a/app/src/main/res/drawable-mdpi/gesture_pgdn.png b/app/src/main/res/drawable-mdpi/gesture_pgdn.png Binary files differnew file mode 100644 index 0000000..61b769b --- /dev/null +++ b/app/src/main/res/drawable-mdpi/gesture_pgdn.png diff --git a/app/src/main/res/drawable-mdpi/gesture_pgup.png b/app/src/main/res/drawable-mdpi/gesture_pgup.png Binary files differnew file mode 100644 index 0000000..0e7f1ca --- /dev/null +++ b/app/src/main/res/drawable-mdpi/gesture_pgup.png diff --git a/app/src/main/res/drawable-mdpi/gesture_scrollback.png b/app/src/main/res/drawable-mdpi/gesture_scrollback.png Binary files differnew file mode 100644 index 0000000..714e626 --- /dev/null +++ b/app/src/main/res/drawable-mdpi/gesture_scrollback.png diff --git a/app/src/main/res/drawable-mdpi/gesture_scrollforward.png b/app/src/main/res/drawable-mdpi/gesture_scrollforward.png Binary files differnew file mode 100644 index 0000000..0172e45 --- /dev/null +++ b/app/src/main/res/drawable-mdpi/gesture_scrollforward.png diff --git a/app/src/main/res/drawable/highlight_disabled_pressed.9.png b/app/src/main/res/drawable-mdpi/highlight_disabled_pressed.9.png Binary files differindex 807fcb5..807fcb5 100644 --- a/app/src/main/res/drawable/highlight_disabled_pressed.9.png +++ b/app/src/main/res/drawable-mdpi/highlight_disabled_pressed.9.png diff --git a/app/src/main/res/layout-large/act_console.xml b/app/src/main/res/layout-large/act_console.xml index 6e7ab14..297d0b1 100644 --- a/app/src/main/res/layout-large/act_console.xml +++ b/app/src/main/res/layout-large/act_console.xml @@ -53,7 +53,7 @@ android:text="@string/terminal_no_hosts_connected" android:textAppearance="?android:attr/textAppearanceMedium"/> - <android.support.v4.view.ViewPager + <org.connectbot.util.TerminalViewPager android:id="@+id/console_flip" android:layout_width="fill_parent" android:layout_height="fill_parent" diff --git a/app/src/main/res/layout-sw500dp/act_hints.xml b/app/src/main/res/layout-sw500dp/act_hints.xml new file mode 100644 index 0000000..9a32dea --- /dev/null +++ b/app/src/main/res/layout-sw500dp/act_hints.xml @@ -0,0 +1,261 @@ +<!-- + ~ 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. + --> + +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context="org.connectbot.HintsActivity"> + + <LinearLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="10dip" + > + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/host_shortcuts_header" + android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_marginTop="10dp"/> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/host_shortcuts_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_marginTop="5dp" + android:layout_marginBottom="5dp"/> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/scroll_hints_header" + android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_marginTop="10dp"/> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/scroll_hints_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_marginTop="5dp" + android:layout_marginBottom="5dp"/> + + <RelativeLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:gravity="center"> + <ImageView + android:id="@+id/scrollback_image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/gesture_scrollback" + android:contentDescription="@string/scrolling_back" + android:layout_marginTop="10dp" + android:layout_marginRight="10dp" + android:layout_marginEnd="10dp" + android:layout_gravity="center_horizontal"/> + + <TextView + android:id="@+id/scrollback_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:text="@string/scrolling_back" + android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_marginBottom="5dp" + android:layout_alignStart="@id/scrollback_image" + android:layout_alignLeft="@id/scrollback_image" + android:layout_alignEnd="@id/scrollback_image" + android:layout_alignRight="@id/scrollback_image" + android:layout_below="@id/scrollback_image" + android:gravity="center_horizontal"/> + + <ImageView + android:id="@+id/scrollforward_image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/gesture_scrollforward" + android:contentDescription="@string/scrolling_forward" + android:layout_marginTop="10dp" + android:layout_marginLeft="10dp" + android:layout_marginStart="10dp" + android:layout_toRightOf="@id/scrollback_image" + android:layout_toEndOf="@id/scrollback_image"/> + + <TextView + android:id="@+id/scrollforward_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:text="@string/scrolling_forward" + android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_marginBottom="5dp" + android:layout_below="@id/scrollforward_image" + android:layout_alignStart="@id/scrollforward_image" + android:layout_alignLeft="@id/scrollforward_image" + android:layout_alignEnd="@id/scrollforward_image" + android:layout_alignRight="@id/scrollforward_image" + android:gravity="center_horizontal"/> + </RelativeLayout> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/page_updn_header" + android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_marginTop="10dp"/> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/page_updn_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_marginTop="5dp" + android:layout_marginBottom="5dp"/> + + <RelativeLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:gravity="center"> + <ImageView + android:id="@+id/pgup_image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/gesture_pgup" + android:contentDescription="@string/page_up" + android:layout_marginTop="10dp" + android:layout_marginRight="10dp" + android:layout_marginEnd="10dp" + android:layout_gravity="center_horizontal"/> + + <TextView + android:id="@+id/pgup_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:text="@string/page_up" + android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_marginBottom="5dp" + android:layout_alignStart="@id/pgup_image" + android:layout_alignLeft="@id/pgup_image" + android:layout_alignEnd="@id/pgup_image" + android:layout_alignRight="@id/pgup_image" + android:layout_below="@id/pgup_image" + android:gravity="center_horizontal"/> + + <ImageView + android:id="@+id/pgdn_image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/gesture_pgdn" + android:contentDescription="@string/page_down" + android:layout_marginTop="10dp" + android:layout_marginLeft="10dp" + android:layout_marginStart="10dp" + android:layout_toRightOf="@id/pgup_image" + android:layout_toEndOf="@id/pgup_image"/> + + <TextView + android:id="@+id/pgdn_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:text="@string/page_down" + android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_marginBottom="5dp" + android:layout_below="@id/pgdn_image" + android:layout_alignStart="@id/pgdn_image" + android:layout_alignLeft="@id/pgdn_image" + android:layout_alignEnd="@id/pgdn_image" + android:layout_alignRight="@id/pgdn_image" + android:gravity="center_horizontal"/> + </RelativeLayout> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/switching_hosts_header" + android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_marginTop="10dp"/> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/switching_hosts_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_marginTop="5dp" + android:layout_marginBottom="5dp"/> + + <RelativeLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:gravity="center"> + <ImageView + android:id="@+id/prev_host_image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/gesture_hostprev" + android:contentDescription="@string/prev_host" + android:layout_marginTop="10dp" + android:layout_marginRight="10dp" + android:layout_marginEnd="10dp" + android:layout_gravity="center_horizontal"/> + + <TextView + android:id="@+id/prev_host_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:text="@string/prev_host" + android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_marginBottom="5dp" + android:layout_alignStart="@id/prev_host_image" + android:layout_alignLeft="@id/prev_host_image" + android:layout_alignEnd="@id/prev_host_image" + android:layout_alignRight="@id/prev_host_image" + android:layout_below="@id/prev_host_image" + android:gravity="center_horizontal"/> + + <ImageView + android:id="@+id/next_host_image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/gesture_hostnext" + android:contentDescription="@string/next_host" + android:layout_marginTop="10dp" + android:layout_marginLeft="10dp" + android:layout_marginStart="10dp" + android:layout_toRightOf="@id/prev_host_image" + android:layout_toEndOf="@id/prev_host_image"/> + + <TextView + android:id="@+id/next_host_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:text="@string/next_host" + android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_marginBottom="10dp" + android:layout_below="@id/next_host_image" + android:layout_alignStart="@id/next_host_image" + android:layout_alignLeft="@id/next_host_image" + android:layout_alignEnd="@id/next_host_image" + android:layout_alignRight="@id/next_host_image" + android:gravity="center_horizontal"/> + </RelativeLayout> + + </LinearLayout> + +</ScrollView> diff --git a/app/src/main/res/layout/act_console.xml b/app/src/main/res/layout/act_console.xml index 34f1d42..fea3a00 100644 --- a/app/src/main/res/layout/act_console.xml +++ b/app/src/main/res/layout/act_console.xml @@ -33,7 +33,7 @@ android:text="@string/terminal_no_hosts_connected" android:textAppearance="?android:attr/textAppearanceMedium"/> - <android.support.v4.view.ViewPager + <org.connectbot.util.TerminalViewPager android:id="@+id/console_flip" android:layout_width="fill_parent" android:layout_height="fill_parent" diff --git a/app/src/main/res/layout/act_help.xml b/app/src/main/res/layout/act_help.xml index b4bb808..146eaa1 100644 --- a/app/src/main/res/layout/act_help.xml +++ b/app/src/main/res/layout/act_help.xml @@ -52,5 +52,23 @@ android:textAppearance="?android:attr/textAppearanceMedium" /> + <Button + android:id="@+id/hints_button" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/hints"/> + + <Button + android:id="@+id/shortcuts_button" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/keyboard_shortcuts"/> + + <Button + android:id="@+id/eula_button" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/terms_and_conditions"/> + </LinearLayout> </ScrollView> diff --git a/app/src/main/res/layout/act_hints.xml b/app/src/main/res/layout/act_hints.xml new file mode 100644 index 0000000..2e45c37 --- /dev/null +++ b/app/src/main/res/layout/act_hints.xml @@ -0,0 +1,189 @@ +<!-- + ~ 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. + --> + +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context="org.connectbot.HintsActivity"> + + <LinearLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="10dip" + > + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/host_shortcuts_header" + android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_marginTop="10dp"/> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/host_shortcuts_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_marginTop="5dp" + android:layout_marginBottom="5dp"/> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/scroll_hints_header" + android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_marginTop="10dp"/> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/scroll_hints_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_marginTop="5dp" + android:layout_marginBottom="5dp"/> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/gesture_scrollback" + android:contentDescription="@string/scrolling_back" + android:layout_marginTop="10dp" + android:layout_gravity="center_horizontal"/> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/scrolling_back" + android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_marginBottom="5dp" + android:gravity="center_horizontal"/> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/gesture_scrollforward" + android:contentDescription="@string/scrolling_forward" + android:layout_marginTop="10dp" + android:layout_gravity="center_horizontal"/> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/scrolling_forward" + android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_marginBottom="5dp" + android:gravity="center_horizontal"/> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/page_updn_header" + android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_marginTop="10dp"/> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/page_updn_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_marginTop="5dp" + android:layout_marginBottom="5dp"/> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/gesture_pgup" + android:contentDescription="@string/page_up" + android:layout_marginTop="10dp" + android:layout_gravity="center_horizontal"/> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/page_up" + android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_marginBottom="5dp" + android:gravity="center_horizontal"/> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/gesture_pgdn" + android:contentDescription="@string/page_down" + android:layout_marginTop="10dp" + android:layout_gravity="center_horizontal"/> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/page_down" + android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_marginBottom="5dp" + android:gravity="center_horizontal"/> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/switching_hosts_header" + android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_marginTop="10dp"/> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/switching_hosts_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_marginTop="5dp" + android:layout_marginBottom="5dp"/> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/gesture_hostprev" + android:contentDescription="@string/prev_host" + android:layout_marginTop="10dp" + android:layout_gravity="center_horizontal"/> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/prev_host" + android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_marginBottom="5dp" + android:gravity="center_horizontal"/> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/gesture_hostnext" + android:contentDescription="@string/next_host" + android:layout_marginTop="10dp" + android:layout_gravity="center_horizontal"/> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/next_host" + android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_marginBottom="5dp" + android:gravity="center_horizontal"/> + + </LinearLayout> + +</ScrollView> diff --git a/app/src/main/res/layout/dia_keyboard_shortcuts.xml b/app/src/main/res/layout/dia_keyboard_shortcuts.xml index b6fb589..fca66b2 100644 --- a/app/src/main/res/layout/dia_keyboard_shortcuts.xml +++ b/app/src/main/res/layout/dia_keyboard_shortcuts.xml @@ -30,7 +30,7 @@ android:stretchColumns="1"> <TableRow> <TextView - android:text="Ctrl-Shift-V" + android:text="@string/paste_shortcut" android:padding="3dip" android:textAppearance="?android:attr/textAppearanceMedium" /> @@ -45,7 +45,7 @@ <TableRow> <TextView - android:text="Ctrl and +" + android:text="@string/increase_font_shortcut" android:padding="3dip" android:textAppearance="?android:attr/textAppearanceMedium" /> @@ -60,7 +60,7 @@ <TableRow> <TextView - android:text="Ctrl and -" + android:text="@string/decrease_font_shortcut" android:padding="3dip" android:textAppearance="?android:attr/textAppearanceMedium" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 648d5f7..7d44f8d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -60,12 +60,57 @@ <!-- Title for the help page with the terms & conditions of the app. --> <string name="terms_and_conditions">"Terms & Conditions"</string> + <!-- Title for the help page with the hints about the app. --> + <string name="hints">"Hints"</string> + + <!-- Header text for instructions about creating host shortcuts. --> + <string name="host_shortcuts_header">"Host Shortcuts"</string> + + <!-- Instructions about creating host shortcuts. --> + <string name="host_shortcuts_content">"Long-press on your Android desktop to create direct shortcuts to frequently-used SSH hosts."</string> + + <!-- Header text for instructions about scrolling the terminal. --> + <string name="scroll_hints_header">"Scroll back / Scroll forward"</string> + + <!-- Instructions about scrolling the terminal. --> + <string name="scroll_hints_content">"Swiping your finger up on the right side of the screen allows you to scroll backward and forward in the local terminal buffer history."</string> + + <!-- Captions for images showing the scroll back/forward gestures. --> + <string name="scrolling_back">"Scrolling back"</string> + <string name="scrolling_forward">"Scrolling forward"</string> + + <!-- Header text for instructions about paging up and down in the terminal. --> + <string name="page_updn_header">"Page Up / Page Down"</string> + + <!-- Instructions about paging up and down in the terminal. --> + <string name="page_updn_content">"Swiping your finger up and down on the left third of the screen will send a page up and page down key to the remote host. Many programs map this to scrolling back into history such as irssi or tinyfugue."</string> + + <!-- Captions for images showing the page up/down gestures. --> + <string name="page_up">"Page up"</string> + <string name="page_down">"Page down"</string> + + !-- Header text for instructions about switching to the next and previous host in the terminal. --> + <string name="switching_hosts_header">"Switching Hosts"</string> + + <!-- Instructions about switching to the next and previous host in the terminal. --> + <string name="switching_hosts_content">"Swiping your finger from one side of the screen to the other will switch between currently connected hosts."</string> + + <!-- Captions for images showing the next/previous host gestures. --> + <string name="next_host">"Next host"</string> + <string name="prev_host">"Previous host"</string> + <!-- Title for the help dialog showing keyboard shortcuts. --> <string name="keyboard_shortcuts">"Keyboard Shortcuts"</string> <!-- Text in a keyboard shortcuts list lined up to keys which increase the terminal font-size. --> <string name="increase_font_size">"Increase Font Size"</string> + <!-- Keyboard shortcut for increasing the font size. --> + <string name="increase_font_shortcut">Ctrl and +</string> <!-- Text in a keyboard shortcuts list lined up to keys which decrease the terminal font-size. --> <string name="decrease_font_size">"Decrease Font Size"</string> + <!-- Keyboard shortcut for decreasing the font size. --> + <string name="decrease_font_shortcut">Ctrl and -</string> + <!-- Keyboard shortcut for pasting into the terminal. --> + <string name="paste_shortcut">Ctrl-Shift-V</string> <string name="pubkey_generate">"Generate"</string> <string name="pubkey_import">"Import"</string> diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index d1801c5..88b9855 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -50,7 +50,7 @@ android:summary="@string/pref_emulation_summary" android:entries="@array/list_emulation_modes" android:entryValues="@array/list_emulation_modes" - android:defaultValue="screen" + android:defaultValue="xterm-256color" /> <EditTextPreference |