From fbe5b14ebf73f906b7df857f546e527bd42bef76 Mon Sep 17 00:00:00 2001 From: Jeremy Klein Date: Wed, 5 Aug 2015 13:38:18 -0700 Subject: [PATCH 1/5] ConsoleActivity: Switch from ViewFlipper to ViewPager. This simplifies the code for creating terminals, removes lots of code, and totally eliminates the need for swipe handling. From the UI side, the swipe now sticks to the user's finger and looks much nicer. It will also make adding tabs much simpler. --- .../java/org/connectbot/ConsoleActivity.java | 365 +++++++----------- app/src/main/res/layout/act_console.xml | 10 +- 2 files changed, 151 insertions(+), 224 deletions(-) diff --git a/app/src/main/java/org/connectbot/ConsoleActivity.java b/app/src/main/java/org/connectbot/ConsoleActivity.java index 50c1a217..5e41b760 100644 --- a/app/src/main/java/org/connectbot/ConsoleActivity.java +++ b/app/src/main/java/org/connectbot/ConsoleActivity.java @@ -20,6 +20,7 @@ import java.lang.ref.WeakReference; import java.util.List; +import org.connectbot.bean.HostBean; import org.connectbot.bean.SelectionArea; import org.connectbot.service.PromptHelper; import org.connectbot.service.TerminalBridge; @@ -47,6 +48,8 @@ import android.os.Message; import android.preference.PreferenceManager; import android.support.v4.view.MotionEventCompat; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; import android.text.ClipboardManager; import android.util.Log; import android.view.ContextMenu; @@ -63,6 +66,7 @@ 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; import android.view.animation.Animation; @@ -78,7 +82,6 @@ import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; -import android.widget.ViewFlipper; import de.mud.terminal.vt320; public class ConsoleActivity extends Activity { @@ -90,12 +93,9 @@ public class ConsoleActivity extends Activity { private static final float MAX_CLICK_DISTANCE = 25f; private static final int KEYBOARD_DISPLAY_TIME = 1500; - // Direction to shift the ViewFlipper - private static final int SHIFT_LEFT = 0; - private static final int SHIFT_RIGHT = 1; - - protected ViewFlipper flip = null; + protected ViewPager pager = null; protected TerminalManager bound = null; + protected TerminalPagerAdapter adapter = null; protected LayoutInflater inflater = null; private SharedPreferences prefs = null; @@ -120,7 +120,7 @@ public class ConsoleActivity extends Activity { private TextView empty; - private Animation slide_left_in, slide_left_out, slide_right_in, slide_right_out, fade_stay_hidden, fade_out_delayed; + private Animation fade_out_delayed; private Animation keyboard_fade_in, keyboard_fade_out; private float lastX, lastY; @@ -150,9 +150,6 @@ public void onServiceConnected(ComponentName className, IBinder service) { bound.disconnectHandler = disconnectHandler; bound.setResizeAllowed(true); - // clear out any existing bridges and record requested index - flip.removeAllViews(); - final String requestedNickname = (requested != null) ? requested.getFragment() : null; int requestedIndex = 0; @@ -169,20 +166,13 @@ public void onServiceConnected(ComponentName className, IBinder service) { } // create views for all bridges on this service - for (TerminalBridge bridge : bound.getBridges()) { - - final int currentIndex = addNewTerminalView(bridge); - - // check to see if this bridge was requested - if (bridge == requestedBridge) - requestedIndex = currentIndex; - } + adapter.notifyDataSetChanged(); + requestedIndex = bound.getBridges().indexOf(requestedBridge); - setDisplayedTerminal(requestedIndex); + pager.setCurrentItem(requestedIndex == -1 ? 0 : requestedIndex); } public void onServiceDisconnected(ComponentName className) { - flip.removeAllViews(); updateEmptyVisible(); bound = null; } @@ -214,42 +204,26 @@ public void handleMessage(Message msg) { * @param bridge */ private void closeBridge(final TerminalBridge bridge) { - synchronized (flip) { - final int flipIndex = getFlipIndex(bridge); - - if (flipIndex >= 0) { - if (flip.getDisplayedChild() == flipIndex) { - shiftCurrentTerminal(SHIFT_LEFT); - } - flip.removeViewAt(flipIndex); - - /* TODO Remove this workaround when ViewFlipper is fixed to listen - * to view removals. Android Issue 1784 - */ - final int numChildren = flip.getChildCount(); - if (flip.getDisplayedChild() >= numChildren && - numChildren > 0) { - flip.setDisplayedChild(numChildren - 1); - } - - updateEmptyVisible(); - } + synchronized (pager) { + adapter.notifyDataSetChanged(); + updateEmptyVisible(); // If we just closed the last bridge, go back to the previous activity. - if (flip.getChildCount() == 0) { + if (pager.getChildCount() == 0) { finish(); } } } protected View findCurrentView(int id) { - View view = flip.getCurrentView(); + if (adapter.getCount() == 0) return null; + View view = pager.getChildAt(pager.getCurrentItem()); if (view == null) return null; return view.findViewById(id); } protected PromptHelper getCurrentPromptHelper() { - View view = findCurrentView(R.id.console_flip); + View view = adapter.getCurrentTerminalView(); if (!(view instanceof TerminalView)) return null; return ((TerminalView) view).bridge.promptHelper; } @@ -326,8 +300,20 @@ public void onCreate(Bundle icicle) { inflater = LayoutInflater.from(this); - flip = (ViewFlipper) findViewById(R.id.console_flip); - registerForContextMenu(flip); + pager = (ViewPager) findViewById(R.id.console_flip); + registerForContextMenu(pager); + pager.addOnPageChangeListener( + new ViewPager.SimpleOnPageChangeListener() { + @Override + public void onPageSelected(int position) { + View overlay = findCurrentView(R.id.terminal_overlay); + if (overlay != null) + overlay.startAnimation(fade_out_delayed); + updateDefault(); + updatePromptVisible(); + } + }); + empty = (TextView) findViewById(android.R.id.empty); stringPromptGroup = (RelativeLayout) findViewById(R.id.console_password_group); @@ -376,14 +362,7 @@ public void onClick(View v) { } }); - // preload animations for terminal switching - slide_left_in = AnimationUtils.loadAnimation(this, R.anim.slide_left_in); - slide_left_out = AnimationUtils.loadAnimation(this, R.anim.slide_left_out); - slide_right_in = AnimationUtils.loadAnimation(this, R.anim.slide_right_in); - slide_right_out = AnimationUtils.loadAnimation(this, R.anim.slide_right_out); - fade_out_delayed = AnimationUtils.loadAnimation(this, R.anim.fade_out_delayed); - fade_stay_hidden = AnimationUtils.loadAnimation(this, R.anim.fade_stay_hidden); // Preload animation for keyboard button keyboard_fade_in = AnimationUtils.loadAnimation(this, R.anim.keyboard_fade_in); @@ -396,11 +375,11 @@ public void onClick(View v) { mKeyboardButton = (ImageView) findViewById(R.id.button_keyboard); mKeyboardButton.setOnClickListener(new OnClickListener() { public void onClick(View view) { - View flip = findCurrentView(R.id.console_flip); - if (flip == null) + View terminal = adapter.getCurrentTerminalView(); + if (terminal == null) return; - inputManager.showSoftInput(flip, InputMethodManager.SHOW_FORCED); + inputManager.showSoftInput(terminal, InputMethodManager.SHOW_FORCED); hideEmulatedKeys(); } }); @@ -408,9 +387,8 @@ public void onClick(View view) { final ImageView ctrlButton = (ImageView) findViewById(R.id.button_ctrl); ctrlButton.setOnClickListener(new OnClickListener() { public void onClick(View view) { - View flip = findCurrentView(R.id.console_flip); - if (flip == null) return; - TerminalView terminal = (TerminalView) flip; + TerminalView terminal = adapter.getCurrentTerminalView(); + if (terminal == null) return; TerminalKeyListener handler = terminal.bridge.getKeyHandler(); handler.metaPress(TerminalKeyListener.OUR_CTRL_ON, true); @@ -421,9 +399,8 @@ public void onClick(View view) { final ImageView escButton = (ImageView) findViewById(R.id.button_esc); escButton.setOnClickListener(new OnClickListener() { public void onClick(View view) { - View flip = findCurrentView(R.id.console_flip); - if (flip == null) return; - TerminalView terminal = (TerminalView) flip; + TerminalView terminal = adapter.getCurrentTerminalView(); + if (terminal == null) return; TerminalKeyListener handler = terminal.bridge.getKeyHandler(); handler.sendEscape(); @@ -434,9 +411,8 @@ public void onClick(View view) { final ImageView tabButton = (ImageView) findViewById(R.id.button_tab); tabButton.setOnClickListener(new OnClickListener() { public void onClick(View view) { - View flip = findCurrentView(R.id.console_flip); - if (flip == null) return; - TerminalView terminal = (TerminalView) flip; + TerminalView terminal = adapter.getCurrentTerminalView(); + if (terminal == null) return; TerminalKeyListener handler = terminal.bridge.getKeyHandler(); handler.sendTab(); @@ -462,35 +438,10 @@ public void onMenuVisibilityChanged(boolean isVisible) { final GestureDetector detect = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() { private float totalY = 0; - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - - final float distx = e2.getRawX() - e1.getRawX(); - final float disty = e2.getRawY() - e1.getRawY(); - final int goalwidth = flip.getWidth() / 2; - - // need to slide across half of display to trigger console change - // make sure user kept a steady hand horizontally - if (Math.abs(disty) < (flip.getHeight() / 4)) { - if (distx > goalwidth) { - shiftCurrentTerminal(SHIFT_RIGHT); - return true; - } - - if (distx < -goalwidth) { - shiftCurrentTerminal(SHIFT_LEFT); - return true; - } - - } - - return super.onFling(e1, e2, velocityX, velocityY); - } - @Override public void onLongPress(MotionEvent e) { super.onLongPress(e); - openContextMenu(flip); + openContextMenu(pager); } @@ -513,9 +464,9 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d int touchSlop = ViewConfiguration.get(ConsoleActivity.this).getScaledTouchSlop(); if (Math.abs(e1.getX() - e2.getX()) < touchSlop * 4) { - View flip = findCurrentView(R.id.console_flip); - if (flip == null) return false; - TerminalView terminal = (TerminalView) flip; + 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 @@ -523,7 +474,7 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d final int moved = (int) (totalY / terminal.bridge.charHeight); // consume as scrollback only if towards right half of screen - if (e2.getX() > flip.getWidth() / 2) { + if (e2.getX() > view.getWidth() / 2) { if (moved != 0) { int base = terminal.bridge.buffer.getWindowBase(); terminal.bridge.buffer.setWindowBase(base + moved); @@ -554,8 +505,8 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d }); - flip.setLongClickable(true); - flip.setOnTouchListener(new OnTouchListener() { + pager.setLongClickable(true); + pager.setOnTouchListener(new OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { @@ -564,17 +515,17 @@ public boolean onTouch(View v, MotionEvent event) { MotionEventCompat.getSource(event) == InputDevice.SOURCE_MOUSE && 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: - // Let the context menu show on right click. - return false; - case MotionEvent.BUTTON_TERTIARY: - // Middle click pastes. - pasteIntoTerminal(); - return true; + 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; } } @@ -652,11 +603,13 @@ public boolean onTouch(View v, MotionEvent event) { } // pass any touch events back to detector - detect.onTouchEvent(event); - return true; + return detect.onTouchEvent(event); } }); + + adapter = new TerminalPagerAdapter(); + pager.setAdapter(adapter); } /** @@ -691,7 +644,7 @@ private void configureOrientation() { public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); - View view = findCurrentView(R.id.console_flip); + View view = adapter.getCurrentTerminalView(); final boolean activeTerminal = (view instanceof TerminalView); boolean sessionOpen = false; boolean disconnected = false; @@ -716,7 +669,7 @@ public boolean onCreateOptionsMenu(Menu menu) { disconnect.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { // disconnect or close the currently visible session - TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip); + TerminalView terminalView = (TerminalView) adapter.getCurrentTerminalView(); TerminalBridge bridge = terminalView.bridge; bridge.dispatchDisconnect(true); @@ -756,7 +709,7 @@ public boolean onMenuItemClick(MenuItem item) { portForward.setEnabled(sessionOpen && canForwardPorts); portForward.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { - TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip); + TerminalView terminalView = (TerminalView) adapter.getCurrentTerminalView(); TerminalBridge bridge = terminalView.bridge; Intent intent = new Intent(ConsoleActivity.this, PortForwardListActivity.class); @@ -773,7 +726,7 @@ public boolean onMenuItemClick(MenuItem item) { urlscan.setEnabled(activeTerminal); urlscan.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { - final TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip); + final TerminalView terminalView = (TerminalView) adapter.getCurrentTerminalView(); List urls = terminalView.bridge.scanForURLs(); @@ -799,7 +752,7 @@ public boolean onMenuItemClick(MenuItem item) { resize.setEnabled(sessionOpen); resize.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { - final TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip); + final TerminalView terminalView = (TerminalView) adapter.getCurrentTerminalView(); final View resizeView = inflater.inflate(R.layout.dia_resize, null, false); new AlertDialog.Builder(ConsoleActivity.this) @@ -837,7 +790,7 @@ public boolean onPrepareOptionsMenu(Menu menu) { setVolumeControlStream(AudioManager.STREAM_NOTIFICATION); - final View view = findCurrentView(R.id.console_flip); + final View view = adapter.getCurrentTerminalView(); boolean activeTerminal = (view instanceof TerminalView); boolean sessionOpen = false; boolean disconnected = false; @@ -886,7 +839,7 @@ public void onOptionsMenuClosed(Menu menu) { @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { - final View view = findCurrentView(R.id.console_flip); + final View view = adapter.getCurrentTerminalView(); boolean activeTerminal = (view instanceof TerminalView); boolean sessionOpen = false; @@ -971,7 +924,7 @@ protected void onNewIntent(Intent intent) { TerminalBridge requestedBridge = bound.getConnectedBridge(requested.getFragment()); int requestedIndex = 0; - synchronized (flip) { + synchronized (pager) { if (requestedBridge == null) { // If we didn't find the requested connection, try opening it @@ -985,15 +938,16 @@ protected void onNewIntent(Intent intent) { return; } - requestedIndex = addNewTerminalView(requestedBridge); + adapter.notifyDataSetChanged(); + requestedIndex = adapter.getCount(); } else { - final int flipIndex = getFlipIndex(requestedBridge); + final int flipIndex = bound.getBridges().indexOf(requestedBridge); if (flipIndex > requestedIndex) { requestedIndex = flipIndex; } } - setDisplayedTerminal(requestedIndex); + pager.setCurrentItem(requestedIndex); } } @@ -1006,7 +960,7 @@ public void onStop() { private void startCopyMode() { // mark as copying and reset any previous bounds - TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip); + TerminalView terminalView = (TerminalView) adapter.getCurrentTerminalView(); copySource = terminalView.bridge; SelectionArea area = copySource.getSelectionArea(); @@ -1019,42 +973,6 @@ private void startCopyMode() { copySource.redraw(); } - protected void shiftCurrentTerminal(final int direction) { - View overlay; - synchronized (flip) { - boolean shouldAnimate = flip.getChildCount() > 1; - - // Only show animation if there is something else to go to. - if (shouldAnimate) { - // keep current overlay from popping up again - overlay = findCurrentView(R.id.terminal_overlay); - if (overlay != null) - overlay.startAnimation(fade_stay_hidden); - - if (direction == SHIFT_LEFT) { - flip.setInAnimation(slide_left_in); - flip.setOutAnimation(slide_left_out); - flip.showNext(); - } else if (direction == SHIFT_RIGHT) { - flip.setInAnimation(slide_right_in); - flip.setOutAnimation(slide_right_out); - flip.showPrevious(); - } - } - - ConsoleActivity.this.updateDefault(); - - if (shouldAnimate) { - // show overlay on new slide and start fade - overlay = findCurrentView(R.id.terminal_overlay); - if (overlay != null) - overlay.startAnimation(fade_out_delayed); - } - - updatePromptVisible(); - } - } - /** * Save the currently shown {@link TerminalView} as the default. This is * saved back down into {@link TerminalManager} where we can read it again @@ -1062,7 +980,7 @@ protected void shiftCurrentTerminal(final int direction) { */ private void updateDefault() { // update the current default terminal - View view = findCurrentView(R.id.console_flip); + View view = adapter.getCurrentTerminalView(); if (!(view instanceof TerminalView)) return; TerminalView terminal = (TerminalView) view; @@ -1072,7 +990,7 @@ private void updateDefault() { protected void updateEmptyVisible() { // update visibility of empty status message - empty.setVisibility((flip.getChildCount() == 0) ? View.VISIBLE : View.GONE); + empty.setVisibility((pager.getChildCount() == 0) ? View.VISIBLE : View.GONE); } /** @@ -1080,7 +998,7 @@ protected void updateEmptyVisible() { */ protected void updatePromptVisible() { // check if our currently-visible terminalbridge is requesting any prompt services - View view = findCurrentView(R.id.console_flip); + View view = adapter.getCurrentTerminalView(); // Hide all the prompts in case a prompt request was canceled hideAllPrompts(); @@ -1168,7 +1086,7 @@ public void onConfigurationChanged(Configuration newConfig) { private void pasteIntoTerminal() { // force insert of clipboard text into current console - TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip); + TerminalView terminalView = (TerminalView) adapter.getCurrentTerminalView(); TerminalBridge bridge = terminalView.bridge; // pull string from clipboard and generate all events to force down @@ -1176,75 +1094,84 @@ private void pasteIntoTerminal() { bridge.injectString(clip); } - /** - * Adds a new TerminalBridge to the current set of views in our ViewFlipper. - * - * @param bridge TerminalBridge to add to our ViewFlipper - * @return the child index of the new view in the ViewFlipper - */ - private int addNewTerminalView(TerminalBridge bridge) { - // let them know about our prompt handler services - bridge.promptHelper.setHandler(promptHandler); - - // inflate each terminal view - RelativeLayout view = (RelativeLayout) inflater.inflate(R.layout.item_terminal, flip, false); - - // set the terminal overlay text - TextView overlay = (TextView) view.findViewById(R.id.terminal_overlay); - overlay.setText(bridge.host.getNickname()); - - // and add our terminal view control, using index to place behind overlay - TerminalView terminal = new TerminalView(ConsoleActivity.this, bridge); - terminal.setId(R.id.console_flip); - view.addView(terminal, 0); - - synchronized (flip) { - // finally attach to the flipper - flip.addView(view); - return flip.getChildCount() - 1; + public class TerminalPagerAdapter extends PagerAdapter { + @Override + public int getCount() { + if (bound != null) { + return bound.getBridges().size(); + } else { + return 0; + } } - } - private int getFlipIndex(TerminalBridge bridge) { - synchronized (flip) { - final int children = flip.getChildCount(); - for (int i = 0; i < children; i++) { - final View view = flip.getChildAt(i).findViewById(R.id.console_flip); + @Override + public Object instantiateItem(ViewGroup container, int position) { + if (bound == null || bound.getBridges().size() <= position) { + Log.w(TAG, "Activity not bound when creating TerminalView."); + } + TerminalBridge bridge = bound.getBridges().get(position); + bridge.promptHelper.setHandler(promptHandler); - if (view == null || !(view instanceof TerminalView)) { - // How did that happen? - continue; - } + // inflate each terminal view + RelativeLayout view = (RelativeLayout) inflater.inflate( + R.layout.item_terminal, container, false); + + // set the terminal overlay text + TextView overlay = (TextView) view.findViewById(R.id.terminal_overlay); + 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); + terminal.setId(R.id.console_flip); + view.addView(terminal, 0); + + container.addView(view); + overlay.startAnimation(fade_out_delayed); + return view; + } - final TerminalView tv = (TerminalView) view; + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + final View view = (View) object; + + container.removeView(view); + } - if (tv.bridge == bridge) { - return i; + @Override + public int getItemPosition(Object object) { + final View view = (View) object; + TerminalView terminal = (TerminalView) view.findViewById(R.id.console_flip); + final HostBean host = terminal.bridge.host; + int itemIndex = -1; + int i = 0; + for (TerminalBridge bridge : bound.getBridges()) { + if (bridge.host.equals(host)) { + itemIndex = i; + break; } + i++; + } + if (itemIndex == -1) { + return POSITION_NONE; + } else { + return itemIndex; } } - return -1; - } + @Override + public boolean isViewFromObject(View view, Object object) { + return view == object; + } - /** - * Displays the child in the ViewFlipper at the requestedIndex and updates the prompts. - * - * @param requestedIndex the index of the terminal view to display - */ - private void setDisplayedTerminal(int requestedIndex) { - synchronized (flip) { - try { - // show the requested bridge if found, also fade out overlay - flip.setDisplayedChild(requestedIndex); - flip.getCurrentView().findViewById(R.id.terminal_overlay) - .startAnimation(fade_out_delayed); - } catch (NullPointerException npe) { - Log.d(TAG, "View went away when we were about to display it", npe); - } + @Override + public CharSequence getPageTitle(int position) { + return bound.getBridges().get(position).host.getNickname(); + } - updatePromptVisible(); - updateEmptyVisible(); + public TerminalView getCurrentTerminalView() { + View currentView = pager.getChildAt(pager.getCurrentItem()); + if (currentView == null) return null; + return (TerminalView) currentView.findViewById(R.id.console_flip); } } } diff --git a/app/src/main/res/layout/act_console.xml b/app/src/main/res/layout/act_console.xml index cd06e504..b3f51578 100644 --- a/app/src/main/res/layout/act_console.xml +++ b/app/src/main/res/layout/act_console.xml @@ -33,7 +33,7 @@ android:gravity="center" /> - @@ -147,7 +147,7 @@ android:layout_alignParentStart="true" android:layout_alignParentLeft="true" android:layout_alignParentBottom="true" - android:src="@+drawable/button_ctrl" + android:src="@drawable/button_ctrl" android:contentDescription="@string/image_description_toggle_control_character" /> @@ -158,7 +158,7 @@ android:layout_height="wrap_content" android:layout_toRightOf="@+id/button_ctrl" android:layout_alignParentBottom="true" - android:src="@+drawable/button_esc" + android:src="@drawable/button_esc" android:contentDescription="@string/image_description_send_escape_character" /> @@ -169,7 +169,7 @@ android:layout_height="wrap_content" android:layout_toRightOf="@+id/button_esc" android:layout_alignParentBottom="true" - android:src="@+drawable/button_tab" + android:src="@drawable/button_tab" android:contentDescription="@string/image_description_send_tab_character" /> From dd3a93c0522bb967f22ac24893389bec091dc163 Mon Sep 17 00:00:00 2001 From: Jeremy Klein Date: Fri, 7 Aug 2015 14:48:32 -0700 Subject: [PATCH 2/5] Fix an issue with prompt handling --- .../java/org/connectbot/ConsoleActivity.java | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/connectbot/ConsoleActivity.java b/app/src/main/java/org/connectbot/ConsoleActivity.java index 5e41b760..853d077b 100644 --- a/app/src/main/java/org/connectbot/ConsoleActivity.java +++ b/app/src/main/java/org/connectbot/ConsoleActivity.java @@ -169,7 +169,7 @@ public void onServiceConnected(ComponentName className, IBinder service) { adapter.notifyDataSetChanged(); requestedIndex = bound.getBridges().indexOf(requestedBridge); - pager.setCurrentItem(requestedIndex == -1 ? 0 : requestedIndex); + setDisplayedTerminal(requestedIndex == -1 ? 0 : requestedIndex); } public void onServiceDisconnected(ComponentName className) { @@ -207,6 +207,7 @@ private void closeBridge(final TerminalBridge bridge) { synchronized (pager) { adapter.notifyDataSetChanged(); updateEmptyVisible(); + updatePromptVisible(); // If we just closed the last bridge, go back to the previous activity. if (pager.getChildCount() == 0) { @@ -947,7 +948,7 @@ protected void onNewIntent(Intent intent) { } } - pager.setCurrentItem(requestedIndex); + setDisplayedTerminal(requestedIndex); } } @@ -998,17 +999,17 @@ protected void updateEmptyVisible() { */ protected void updatePromptVisible() { // check if our currently-visible terminalbridge is requesting any prompt services - View view = adapter.getCurrentTerminalView(); + TerminalView view = adapter.getCurrentTerminalView(); // Hide all the prompts in case a prompt request was canceled hideAllPrompts(); - if (!(view instanceof TerminalView)) { + if (view == null) { // we dont have an active view, so hide any prompts return; } - PromptHelper prompt = ((TerminalView) view).bridge.promptHelper; + PromptHelper prompt = view.bridge.promptHelper; if (String.class.equals(prompt.promptRequested)) { stringPromptGroup.setVisibility(View.VISIBLE); @@ -1084,6 +1085,17 @@ public void onConfigurationChanged(Configuration newConfig) { } } + /** + * Displays the child in the ViewPager at the requestedIndex and updates the prompts. + * + * @param requestedIndex the index of the terminal view to display + */ + private void setDisplayedTerminal(int requestedIndex) { + pager.setCurrentItem(requestedIndex); + updatePromptVisible(); + updateEmptyVisible(); + } + private void pasteIntoTerminal() { // force insert of clipboard text into current console TerminalView terminalView = (TerminalView) adapter.getCurrentTerminalView(); @@ -1125,6 +1137,10 @@ public Object instantiateItem(ViewGroup container, int position) { terminal.setId(R.id.console_flip); view.addView(terminal, 0); + // Tag the view with its position so that it can be retrieved later. + // Unfortunately, position here != child position in the ViewPager. + view.setTag(position); + container.addView(view); overlay.startAnimation(fade_out_delayed); return view; @@ -1169,7 +1185,7 @@ public CharSequence getPageTitle(int position) { } public TerminalView getCurrentTerminalView() { - View currentView = pager.getChildAt(pager.getCurrentItem()); + View currentView = pager.findViewWithTag(pager.getCurrentItem()); if (currentView == null) return null; return (TerminalView) currentView.findViewById(R.id.console_flip); } From 50b43262ee0e16377b5738164ef8255c959ed832 Mon Sep 17 00:00:00 2001 From: Jeremy Klein Date: Fri, 7 Aug 2015 16:26:04 -0700 Subject: [PATCH 3/5] notify the adapter of a change on disconnects --- app/src/main/java/org/connectbot/ConsoleActivity.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/connectbot/ConsoleActivity.java b/app/src/main/java/org/connectbot/ConsoleActivity.java index 853d077b..48675e5c 100644 --- a/app/src/main/java/org/connectbot/ConsoleActivity.java +++ b/app/src/main/java/org/connectbot/ConsoleActivity.java @@ -173,6 +173,7 @@ public void onServiceConnected(ComponentName className, IBinder service) { } public void onServiceDisconnected(ComponentName className) { + adapter.notifyDataSetChanged(); updateEmptyVisible(); bound = null; } @@ -195,6 +196,7 @@ public void handleMessage(Message msg) { // they are sending nickname and requested TerminalBridge bridge = (TerminalBridge) msg.obj; + adapter.notifyDataSetChanged(); if (bridge.isAwaitingClose()) closeBridge(bridge); } @@ -205,7 +207,6 @@ public void handleMessage(Message msg) { */ private void closeBridge(final TerminalBridge bridge) { synchronized (pager) { - adapter.notifyDataSetChanged(); updateEmptyVisible(); updatePromptVisible(); From c7f0ab6934e86f3d85d1895c6a71e16173993213 Mon Sep 17 00:00:00 2001 From: Jeremy Klein Date: Fri, 7 Aug 2015 17:14:34 -0700 Subject: [PATCH 4/5] Fix some unnecesary casts and lint errors --- .../java/org/connectbot/ConsoleActivity.java | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/org/connectbot/ConsoleActivity.java b/app/src/main/java/org/connectbot/ConsoleActivity.java index 48675e5c..91abb82c 100644 --- a/app/src/main/java/org/connectbot/ConsoleActivity.java +++ b/app/src/main/java/org/connectbot/ConsoleActivity.java @@ -225,9 +225,9 @@ protected View findCurrentView(int id) { } protected PromptHelper getCurrentPromptHelper() { - View view = adapter.getCurrentTerminalView(); - if (!(view instanceof TerminalView)) return null; - return ((TerminalView) view).bridge.promptHelper; + TerminalView view = adapter.getCurrentTerminalView(); + if (view == null) return null; + return view.bridge.promptHelper; } protected void hideAllPrompts() { @@ -591,8 +591,6 @@ public boolean onTouch(View v, MotionEvent event) { } } - Configuration config = getResources().getConfiguration(); - if (event.getAction() == MotionEvent.ACTION_DOWN) { lastX = event.getX(); lastY = event.getY(); @@ -646,14 +644,14 @@ private void configureOrientation() { public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); - View view = adapter.getCurrentTerminalView(); - final boolean activeTerminal = (view instanceof TerminalView); + TerminalView view = adapter.getCurrentTerminalView(); + final boolean activeTerminal = view != null; boolean sessionOpen = false; boolean disconnected = false; boolean canForwardPorts = false; if (activeTerminal) { - TerminalBridge bridge = ((TerminalView) view).bridge; + TerminalBridge bridge = view.bridge; sessionOpen = bridge.isSessionOpen(); disconnected = bridge.isDisconnected(); canForwardPorts = bridge.canFowardPorts(); @@ -671,7 +669,7 @@ public boolean onCreateOptionsMenu(Menu menu) { disconnect.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { // disconnect or close the currently visible session - TerminalView terminalView = (TerminalView) adapter.getCurrentTerminalView(); + TerminalView terminalView = adapter.getCurrentTerminalView(); TerminalBridge bridge = terminalView.bridge; bridge.dispatchDisconnect(true); @@ -711,7 +709,7 @@ public boolean onMenuItemClick(MenuItem item) { portForward.setEnabled(sessionOpen && canForwardPorts); portForward.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { - TerminalView terminalView = (TerminalView) adapter.getCurrentTerminalView(); + TerminalView terminalView = adapter.getCurrentTerminalView(); TerminalBridge bridge = terminalView.bridge; Intent intent = new Intent(ConsoleActivity.this, PortForwardListActivity.class); @@ -728,7 +726,7 @@ public boolean onMenuItemClick(MenuItem item) { urlscan.setEnabled(activeTerminal); urlscan.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { - final TerminalView terminalView = (TerminalView) adapter.getCurrentTerminalView(); + final TerminalView terminalView = adapter.getCurrentTerminalView(); List urls = terminalView.bridge.scanForURLs(); @@ -754,7 +752,7 @@ public boolean onMenuItemClick(MenuItem item) { resize.setEnabled(sessionOpen); resize.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { - final TerminalView terminalView = (TerminalView) adapter.getCurrentTerminalView(); + final TerminalView terminalView = adapter.getCurrentTerminalView(); final View resizeView = inflater.inflate(R.layout.dia_resize, null, false); new AlertDialog.Builder(ConsoleActivity.this) @@ -792,14 +790,14 @@ public boolean onPrepareOptionsMenu(Menu menu) { setVolumeControlStream(AudioManager.STREAM_NOTIFICATION); - final View view = adapter.getCurrentTerminalView(); - boolean activeTerminal = (view instanceof TerminalView); + final TerminalView view = adapter.getCurrentTerminalView(); + boolean activeTerminal = view != null; boolean sessionOpen = false; boolean disconnected = false; boolean canForwardPorts = false; if (activeTerminal) { - TerminalBridge bridge = ((TerminalView) view).bridge; + TerminalBridge bridge = view.bridge; sessionOpen = bridge.isSessionOpen(); disconnected = bridge.isDisconnected(); canForwardPorts = bridge.canFowardPorts(); @@ -841,12 +839,12 @@ public void onOptionsMenuClosed(Menu menu) { @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { - final View view = adapter.getCurrentTerminalView(); - boolean activeTerminal = (view instanceof TerminalView); + final TerminalView view = adapter.getCurrentTerminalView(); + boolean activeTerminal = view != null; boolean sessionOpen = false; if (activeTerminal) { - TerminalBridge bridge = ((TerminalView) view).bridge; + TerminalBridge bridge = view.bridge; sessionOpen = bridge.isSessionOpen(); } @@ -982,12 +980,11 @@ private void startCopyMode() { */ private void updateDefault() { // update the current default terminal - View view = adapter.getCurrentTerminalView(); - if (!(view instanceof TerminalView)) return; + TerminalView view = adapter.getCurrentTerminalView(); + if (view == null) return; - TerminalView terminal = (TerminalView) view; if (bound == null) return; - bound.defaultBridge = terminal.bridge; + bound.defaultBridge = view.bridge; } protected void updateEmptyVisible() { @@ -1099,7 +1096,7 @@ private void setDisplayedTerminal(int requestedIndex) { private void pasteIntoTerminal() { // force insert of clipboard text into current console - TerminalView terminalView = (TerminalView) adapter.getCurrentTerminalView(); + TerminalView terminalView = adapter.getCurrentTerminalView(); TerminalBridge bridge = terminalView.bridge; // pull string from clipboard and generate all events to force down From 374952ea62dd5199b38da51cc7efe075d999120a Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Fri, 7 Aug 2015 23:25:19 -0600 Subject: [PATCH 5/5] Allow indexing into bridges to assist with ViewPager Since ViewPager seems to have no way to inspect its items, we need to query the adapter for the item at the selected index. --- .../java/org/connectbot/ConsoleActivity.java | 45 ++++++++++++------- .../connectbot/service/TerminalManager.java | 5 ++- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/org/connectbot/ConsoleActivity.java b/app/src/main/java/org/connectbot/ConsoleActivity.java index 91abb82c..e011c932 100644 --- a/app/src/main/java/org/connectbot/ConsoleActivity.java +++ b/app/src/main/java/org/connectbot/ConsoleActivity.java @@ -47,6 +47,7 @@ import android.os.IBinder; import android.os.Message; import android.preference.PreferenceManager; +import android.support.v4.app.ActivityCompat; import android.support.v4.view.MotionEventCompat; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; @@ -197,8 +198,9 @@ public void handleMessage(Message msg) { TerminalBridge bridge = (TerminalBridge) msg.obj; adapter.notifyDataSetChanged(); - if (bridge.isAwaitingClose()) + if (bridge.isAwaitingClose()) { closeBridge(bridge); + } } }; @@ -218,9 +220,10 @@ private void closeBridge(final TerminalBridge bridge) { } protected View findCurrentView(int id) { - if (adapter.getCount() == 0) return null; - View view = pager.getChildAt(pager.getCurrentItem()); - if (view == null) return null; + TerminalView view = adapter.getCurrentTerminalView(); + if (view == null) { + return null; + } return view.findViewById(id); } @@ -308,11 +311,7 @@ public void onCreate(Bundle icicle) { new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageSelected(int position) { - View overlay = findCurrentView(R.id.terminal_overlay); - if (overlay != null) - overlay.startAnimation(fade_out_delayed); - updateDefault(); - updatePromptVisible(); + onTerminalChanged(); } }); @@ -1083,6 +1082,18 @@ public void onConfigurationChanged(Configuration newConfig) { } } + /** + * Called whenever the displayed terminal is changed. + */ + private void onTerminalChanged() { + View overlay = findCurrentView(R.id.terminal_overlay); + if (overlay != null) + overlay.startAnimation(fade_out_delayed); + updateDefault(); + updatePromptVisible(); + ActivityCompat.invalidateOptionsMenu(ConsoleActivity.this); + } + /** * Displays the child in the ViewPager at the requestedIndex and updates the prompts. * @@ -1090,8 +1101,7 @@ public void onConfigurationChanged(Configuration newConfig) { */ private void setDisplayedTerminal(int requestedIndex) { pager.setCurrentItem(requestedIndex); - updatePromptVisible(); - updateEmptyVisible(); + onTerminalChanged(); } private void pasteIntoTerminal() { @@ -1135,9 +1145,8 @@ public Object instantiateItem(ViewGroup container, int position) { terminal.setId(R.id.console_flip); view.addView(terminal, 0); - // Tag the view with its position so that it can be retrieved later. - // Unfortunately, position here != child position in the ViewPager. - view.setTag(position); + // Tag the view with its bridge so it can be retrieved later. + view.setTag(bridge); container.addView(view); overlay.startAnimation(fade_out_delayed); @@ -1157,7 +1166,7 @@ public int getItemPosition(Object object) { TerminalView terminal = (TerminalView) view.findViewById(R.id.console_flip); final HostBean host = terminal.bridge.host; int itemIndex = -1; - int i = 0; + int i = 0; for (TerminalBridge bridge : bound.getBridges()) { if (bridge.host.equals(host)) { itemIndex = i; @@ -1172,6 +1181,10 @@ public int getItemPosition(Object object) { } } + public TerminalBridge getItemAtPosition(int position) { + return bound.getBridges().get(position); + } + @Override public boolean isViewFromObject(View view, Object object) { return view == object; @@ -1183,7 +1196,7 @@ public CharSequence getPageTitle(int position) { } public TerminalView getCurrentTerminalView() { - View currentView = pager.findViewWithTag(pager.getCurrentItem()); + View currentView = pager.findViewWithTag(adapter.getItemAtPosition(pager.getCurrentItem())); if (currentView == null) return null; return (TerminalView) currentView.findViewById(R.id.console_flip); } diff --git a/app/src/main/java/org/connectbot/service/TerminalManager.java b/app/src/main/java/org/connectbot/service/TerminalManager.java index b5d891db..80015611 100644 --- a/app/src/main/java/org/connectbot/service/TerminalManager.java +++ b/app/src/main/java/org/connectbot/service/TerminalManager.java @@ -22,6 +22,7 @@ import java.security.KeyPair; import java.security.PrivateKey; import java.security.PublicKey; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; @@ -70,7 +71,7 @@ public class TerminalManager extends Service implements BridgeDisconnectedListener, OnSharedPreferenceChangeListener { public final static String TAG = "CB.TerminalManager"; - private List bridges = new LinkedList(); + private ArrayList bridges = new ArrayList(); public Map> mHostBridgeMap = new HashMap>(); public Map> mNicknameBridgeMap = @@ -471,7 +472,7 @@ private synchronized void stopIdleTimer() { } } - public List getBridges() { + public ArrayList getBridges() { return bridges; }