Mercurial > 510Connectbot
diff src/com/five_ten_sg/connectbot/ConsoleActivity.java @ 0:0ce5cc452d02
initial version
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Thu, 22 May 2014 10:41:19 -0700 |
parents | |
children | e0f5e706a655 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/five_ten_sg/connectbot/ConsoleActivity.java Thu May 22 10:41:19 2014 -0700 @@ -0,0 +1,1508 @@ +/* + * ConnectBot: simple, powerful, open-source SSH client for Android + * Copyright 2007 Kenny Root, Jeffrey Sharkey + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.five_ten_sg.connectbot; + +import java.io.File; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +import com.five_ten_sg.connectbot.bean.SelectionArea; +import com.five_ten_sg.connectbot.service.PromptHelper; +import com.five_ten_sg.connectbot.service.TerminalBridge; +import com.five_ten_sg.connectbot.service.TerminalKeyListener; +import com.five_ten_sg.connectbot.service.TerminalManager; +import com.five_ten_sg.connectbot.util.FileChooser; +import com.five_ten_sg.connectbot.util.FileChooserCallback; +import com.five_ten_sg.connectbot.util.PreferenceConstants; +import com.five_ten_sg.connectbot.util.TransferThread; +import android.app.Activity; +import android.app.AlertDialog; +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.SharedPreferences; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; +import android.media.AudioManager; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.preference.PreferenceManager; +import android.text.ClipboardManager; +import android.text.InputType; +import android.text.method.PasswordTransformationMethod; +import android.text.method.SingleLineTransformationMethod; +import android.util.FloatMath; +import android.util.Log; +import android.view.GestureDetector; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MenuItem.OnMenuItemClickListener; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnKeyListener; +import android.view.View.OnLongClickListener; +import android.view.View.OnTouchListener; +import android.view.ViewConfiguration; +import android.view.WindowManager; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.inputmethod.InputMethodManager; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +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 implements FileChooserCallback { + public final static String TAG = "ConnectBot.ConsoleActivity"; + + 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 = 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 TerminalManager bound = null; + protected LayoutInflater inflater = null; + + private SharedPreferences prefs = null; + + // determines whether or not menuitem accelerators are bound + // otherwise they collide with an external keyboard's CTRL-char + private boolean hardKeyboard = false; + + // determines whether we are in the fullscreen mode + private static final int FULLSCREEN_ON = 1; + private static final int FULLSCREEN_OFF = 2; + + private int fullScreen; + + protected Uri requested; + + protected ClipboardManager clipboard; + private RelativeLayout stringPromptGroup; + protected EditText stringPrompt; + private TextView stringPromptInstructions; + + private RelativeLayout booleanPromptGroup; + private TextView booleanPrompt; + private Button booleanYes, booleanNo; + + private RelativeLayout keyboardGroup; + private Runnable keyboardGroupHider; + + private TextView empty; + + private Animation slide_left_in, slide_left_out, slide_right_in, slide_right_out, fade_stay_hidden, fade_out_delayed; + + private Animation keyboard_fade_in, keyboard_fade_out; + private float lastX, lastY; + + private InputMethodManager inputManager; + + private MenuItem disconnect, copy, paste, portForward, resize, urlscan, screenCapture, download, upload; + + protected TerminalBridge copySource = null; + private int lastTouchRow, lastTouchCol; + + private boolean forcedOrientation; + + private Handler handler = new Handler(); + + private ImageView mKeyboardButton; + + private ActionBarWrapper actionBar; + private boolean inActionBarMenu = false; + + private ServiceConnection connection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + bound = ((TerminalManager.TerminalBinder) service).getService(); + // let manager know about our event handling services + bound.disconnectHandler = disconnectHandler; + Log.d(TAG, String.format("Connected to TerminalManager and found bridges.size=%d", bound.bridges.size())); + bound.setResizeAllowed(true); + bound.hardKeyboardHidden = (getResources().getConfiguration().hardKeyboardHidden == + Configuration.HARDKEYBOARDHIDDEN_YES); + + // set fullscreen value + if (bound.getFullScreen() == 0) { + setFullScreen(FULLSCREEN_OFF); + } + else if (fullScreen != bound.getFullScreen()) + setFullScreen(bound.getFullScreen()); + + // clear out any existing bridges and record requested index + flip.removeAllViews(); + final String requestedNickname = (requested != null) ? requested.getFragment() : null; + int requestedIndex = -1; + TerminalBridge requestedBridge = bound.getConnectedBridge(requestedNickname); + + // If we didn't find the requested connection, try opening it + if (requestedNickname != null && requestedBridge == null) { + try { + Log.d(TAG, String.format("We couldnt find an existing bridge with URI=%s (nickname=%s), so creating one now", requested.toString(), requestedNickname)); + requestedBridge = bound.openConnection(requested); + } + catch (Exception e) { + Log.e(TAG, "Problem while trying to create new requested bridge from URI", e); + } + } + + // create views for all bridges on this service + for (TerminalBridge bridge : bound.bridges) { + final int currentIndex = addNewTerminalView(bridge); + + // check to see if this bridge was requested + if (bridge == requestedBridge) { + requestedIndex = currentIndex; + // store this bridge as default bridge + bound.defaultBridge = bridge; + } + } + + // if no bridge was requested, try using default bridge + if (requestedIndex < 0) { + requestedIndex = getFlipIndex(bound.defaultBridge); + + if (requestedIndex < 0) + requestedIndex = 0; + } + + setDisplayedTerminal(requestedIndex); + } + public void onServiceDisconnected(ComponentName className) { + // tell each bridge to forget about our prompt handler + synchronized (bound.bridges) { + for (TerminalBridge bridge : bound.bridges) + bridge.promptHelper.setHandler(null); + } + + flip.removeAllViews(); + updateEmptyVisible(); + bound = null; + } + }; + + protected Handler promptHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + // someone below us requested to display a prompt + updatePromptVisible(); + } + }; + + protected Handler disconnectHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + Log.d(TAG, "Someone sending HANDLE_DISCONNECT to parentHandler"); + // someone below us requested to display a password dialog + // they are sending nickname and requested + TerminalBridge bridge = (TerminalBridge)msg.obj; + + if (bridge.isAwaitingClose()) + closeBridge(bridge); + } + }; + + /** + * @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(); + } + + // If we just closed the last bridge, go back to the previous activity. + if (flip.getChildCount() == 0) { + finish(); + } + } + } + + protected View findCurrentView(int id) { + View view = flip.getCurrentView(); + + if (view == null) return null; + + return view.findViewById(id); + } + + protected PromptHelper getCurrentPromptHelper() { + View view = findCurrentView(R.id.console_flip); + + if (!(view instanceof TerminalView)) return null; + + return ((TerminalView)view).bridge.promptHelper; + } + + protected void hideAllPrompts() { + stringPromptGroup.setVisibility(View.GONE); + booleanPromptGroup.setVisibility(View.GONE); + // adjust window back if size was changed during prompt input + View view = findCurrentView(R.id.console_flip); + + if (!(view instanceof TerminalView)) return; + + ((TerminalView)view).bridge.parentChanged((TerminalView)view); + } + + private void showEmulatedKeys() { + keyboardGroup.startAnimation(keyboard_fade_in); + keyboardGroup.setVisibility(View.VISIBLE); + actionBar.show(); + + if (keyboardGroupHider != null) + handler.removeCallbacks(keyboardGroupHider); + + keyboardGroupHider = new Runnable() { + public void run() { + if (keyboardGroup.getVisibility() == View.GONE || inActionBarMenu) + return; + + keyboardGroup.startAnimation(keyboard_fade_out); + keyboardGroup.setVisibility(View.GONE); + actionBar.hide(); + keyboardGroupHider = null; + } + }; + handler.postDelayed(keyboardGroupHider, KEYBOARD_DISPLAY_TIME); + } + + private void hideEmulatedKeys() { + if (keyboardGroupHider != null) + handler.removeCallbacks(keyboardGroupHider); + + keyboardGroup.setVisibility(View.GONE); + actionBar.hide(); + } + + // more like configureLaxMode -- enable network IO on UI thread + private void configureStrictMode() { + try { + Class.forName("android.os.StrictMode"); + StrictModeSetup.run(); + } + catch (ClassNotFoundException e) { + } + } + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + configureStrictMode(); + hardKeyboard = getResources().getConfiguration().keyboard == + Configuration.KEYBOARD_QWERTY; + this.setContentView(R.layout.act_console); + clipboard = (ClipboardManager)getSystemService(CLIPBOARD_SERVICE); + prefs = PreferenceManager.getDefaultSharedPreferences(this); + // TODO find proper way to disable volume key beep if it exists. + setVolumeControlStream(AudioManager.STREAM_MUSIC); + // handle requested console from incoming intent + requested = getIntent().getData(); + inflater = LayoutInflater.from(this); + flip = (ViewFlipper)findViewById(R.id.console_flip); + empty = (TextView)findViewById(android.R.id.empty); + stringPromptGroup = (RelativeLayout) findViewById(R.id.console_password_group); + stringPromptInstructions = (TextView) findViewById(R.id.console_password_instructions); + stringPrompt = (EditText)findViewById(R.id.console_password); + stringPrompt.setOnKeyListener(new OnKeyListener() { + public boolean onKey(View v, int keyCode, KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_UP) return false; + + if (keyCode != KeyEvent.KEYCODE_ENTER) return false; + + // pass collected password down to current terminal + String value = stringPrompt.getText().toString(); + PromptHelper helper = getCurrentPromptHelper(); + + if (helper == null) return false; + + helper.setResponse(value); + // finally clear password for next user + stringPrompt.setText(""); + updatePromptVisible(); + return true; + } + }); + booleanPromptGroup = (RelativeLayout) findViewById(R.id.console_boolean_group); + booleanPrompt = (TextView)findViewById(R.id.console_prompt); + booleanYes = (Button)findViewById(R.id.console_prompt_yes); + booleanYes.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + PromptHelper helper = getCurrentPromptHelper(); + + if (helper == null) return; + + helper.setResponse(Boolean.TRUE); + updatePromptVisible(); + } + }); + booleanNo = (Button)findViewById(R.id.console_prompt_no); + booleanNo.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + PromptHelper helper = getCurrentPromptHelper(); + + if (helper == null) return; + + helper.setResponse(Boolean.FALSE); + updatePromptVisible(); + } + }); + // 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); + keyboard_fade_out = AnimationUtils.loadAnimation(this, R.anim.keyboard_fade_out); + inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + keyboardGroup = (RelativeLayout) findViewById(R.id.keyboard_group); + mKeyboardButton = (ImageView) findViewById(R.id.button_keyboard); + mKeyboardButton.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + View flip = findCurrentView(R.id.console_flip); + + if (flip == null) + return; + + inputManager.showSoftInput(flip, InputMethodManager.SHOW_FORCED); + hideEmulatedKeys(); + } + }); + final ImageView symButton = (ImageView) findViewById(R.id.button_sym); + symButton.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + View flip = findCurrentView(R.id.console_flip); + + if (flip == null) return; + + TerminalView terminal = (TerminalView)flip; + terminal.bridge.showCharPickerDialog(); + keyboardGroup.setVisibility(View.GONE); + } + }); + symButton.setOnLongClickListener(new OnLongClickListener() { + public boolean onLongClick(View view) { + View flip = findCurrentView(R.id.console_flip); + + if (flip == null) return false; + + TerminalView terminal = (TerminalView)flip; + terminal.bridge.showArrowsDialog(); + return true; + } + }); + final ImageView mInputButton = (ImageView) findViewById(R.id.button_input); + mInputButton.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + View flip = findCurrentView(R.id.console_flip); + + if (flip == null) return; + + final TerminalView terminal = (TerminalView)flip; + Thread promptThread = new Thread(new Runnable() { + public void run() { + String inj = getCurrentPromptHelper().requestStringPrompt(null, ""); + terminal.bridge.injectString(inj); + } + }); + promptThread.setName("Prompt"); + promptThread.setDaemon(true); + promptThread.start(); + keyboardGroup.setVisibility(View.GONE); + } + }); + final ImageView ctrlButton = (ImageView) findViewById(R.id.button_ctrl); + ctrlButton.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + View flip = findCurrentView(R.id.console_flip); + + if (flip == null) return; + + TerminalView terminal = (TerminalView)flip; + TerminalKeyListener handler = terminal.bridge.getKeyHandler(); + handler.metaPress(TerminalKeyListener.META_CTRL_ON); + hideEmulatedKeys(); + } + }); + ctrlButton.setOnLongClickListener(new OnLongClickListener() { + public boolean onLongClick(View view) { + View flip = findCurrentView(R.id.console_flip); + + if (flip == null) return false; + + TerminalView terminal = (TerminalView)flip; + terminal.bridge.showCtrlDialog(); + return true; + } + }); + final ImageView escButton = (ImageView) findViewById(R.id.button_esc); + escButton.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + View flip = findCurrentView(R.id.console_flip); + + if (flip == null) return; + + TerminalView terminal = (TerminalView)flip; + TerminalKeyListener handler = terminal.bridge.getKeyHandler(); + handler.sendEscape(); + hideEmulatedKeys(); + } + }); + escButton.setOnLongClickListener(new OnLongClickListener() { + public boolean onLongClick(View view) { + View flip = findCurrentView(R.id.console_flip); + + if (flip == null) return false; + + TerminalView terminal = (TerminalView)flip; + terminal.bridge.showFKeysDialog(); + return true; + } + }); + actionBar = ActionBarWrapper.getActionBar(this); + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.hide(); + actionBar.addOnMenuVisibilityListener(new ActionBarWrapper.OnMenuVisibilityListener() { + public void onMenuVisibilityChanged(boolean isVisible) { + inActionBarMenu = isVisible; + + if (isVisible == false) { + hideEmulatedKeys(); + } + } + }); + // detect fling gestures to switch between terminals + final GestureDetector gestDetect = new GestureDetector(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 false; + } + @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 + if (Math.abs(e1.getX() - e2.getX()) < ViewConfiguration.getTouchSlop() * 4) { + View flip = findCurrentView(R.id.console_flip); + + if (flip == null) return false; + + TerminalView terminal = (TerminalView)flip; + // 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() > flip.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; + } + /* + * Enables longpress and popups menu + * + * @see + * android.view.GestureDetector.SimpleOnGestureListener# + * onLongPress(android.view.MotionEvent) + * + * @return void + */ + @Override + public void onLongPress(MotionEvent e) { + List<String> itemList = new ArrayList<String>(); + final TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip); + + if (terminalView == null) + return; + + final TerminalBridge bridge = terminalView.bridge; + + if (fullScreen == FULLSCREEN_ON) + itemList.add(ConsoleActivity.this + .getResources().getString(R.string.longpress_disable_full_screen_mode)); + else + itemList.add(ConsoleActivity.this + .getResources().getString(R.string.longpress_enable_full_screen_mode)); + + itemList.add(ConsoleActivity.this + .getResources().getString(R.string.longpress_change_font_size)); + + if (prefs.getBoolean(PreferenceConstants.EXTENDED_LONGPRESS, false)) { + itemList.add(ConsoleActivity.this + .getResources().getString(R.string.longpress_arrows_dialog)); + itemList.add(ConsoleActivity.this + .getResources().getString(R.string.longpress_fkeys_dialog)); + itemList.add(ConsoleActivity.this + .getResources().getString(R.string.longpress_ctrl_dialog)); + itemList.add(ConsoleActivity.this + .getResources().getString(R.string.longpress_sym_dialog)); + } + + if (itemList.size() > 0) { + AlertDialog.Builder builder = new AlertDialog.Builder(ConsoleActivity.this); + builder.setTitle(R.string.longpress_select_action); + builder.setItems(itemList.toArray(new CharSequence[itemList.size()]), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int item) { + switch (item) { + case 0: + if (fullScreen == FULLSCREEN_ON) { + setFullScreen(FULLSCREEN_OFF); + } + else + setFullScreen(FULLSCREEN_ON); + + break; + + case 1: + bridge.showFontSizeDialog(); + break; + + case 2: + bridge.showArrowsDialog(); + break; + + case 3: + bridge.showFKeysDialog(); + break; + + case 4: + bridge.showCtrlDialog(); + break; + + case 5: + bridge.showCharPickerDialog(); + } + } + }); + AlertDialog alert = builder.create(); + alert.show(); + } + } + }); + flip.setLongClickable(true); + flip.setOnTouchListener(new OnTouchListener() { + public boolean onTouch(View v, MotionEvent event) { + // when copying, highlight the area + if (copySource != null && copySource.isSelectingForCopy()) { + int row = (int)FloatMath.floor(event.getY() / copySource.charHeight); + int col = (int)FloatMath.floor(event.getX() / copySource.charWidth); + SelectionArea area = copySource.getSelectionArea(); + + 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; + } + } + + Configuration config = getResources().getConfiguration(); + + 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(); + } + + // pass any touch events back to detector + TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip); + return terminalView.mScaleDetector.onTouchEvent(event) | gestDetect.onTouchEvent(event); + } + }); + } + + /** + * + */ + private void configureOrientation() { + String rotateDefault; + + if (getResources().getConfiguration().keyboard == Configuration.KEYBOARD_NOKEYS) + rotateDefault = PreferenceConstants.ROTATION_PORTRAIT; + else + rotateDefault = PreferenceConstants.ROTATION_LANDSCAPE; + + String rotate = prefs.getString(PreferenceConstants.ROTATION, rotateDefault); + + if (PreferenceConstants.ROTATION_DEFAULT.equals(rotate)) + rotate = rotateDefault; + + // request a forced orientation if requested by user + if (PreferenceConstants.ROTATION_LANDSCAPE.equals(rotate)) { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + forcedOrientation = true; + } + else if (PreferenceConstants.ROTATION_PORTRAIT.equals(rotate)) { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + forcedOrientation = true; + } + else { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); + forcedOrientation = false; + } + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + View view = findCurrentView(R.id.console_flip); + final boolean activeTerminal = (view instanceof TerminalView); + boolean sessionOpen = false; + boolean disconnected = false; + boolean canForwardPorts = false; + boolean canTransferFiles = false; + + if (activeTerminal) { + TerminalBridge bridge = ((TerminalView) view).bridge; + sessionOpen = bridge.isSessionOpen(); + disconnected = bridge.isDisconnected(); + canForwardPorts = bridge.canFowardPorts(); + canTransferFiles = bridge.canTransferFiles(); + } + + menu.setQwertyMode(true); + + if (!PreferenceConstants.PRE_HONEYCOMB) { + MenuItem ctrlKey = menu.add(getString(R.string.fullscreen)); + ctrlKey.setEnabled(activeTerminal); + ctrlKey.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + ctrlKey.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem menuItem) { + if (fullScreen == FULLSCREEN_ON) { + setFullScreen(FULLSCREEN_OFF); + } + else + setFullScreen(FULLSCREEN_ON); + + return true; + } + }); + } + + disconnect = menu.add(R.string.list_host_disconnect); + + if (hardKeyboard) + disconnect.setAlphabeticShortcut('w'); + + if (!sessionOpen && disconnected) + disconnect.setTitle(R.string.console_menu_close); + + disconnect.setEnabled(activeTerminal); + disconnect.setIcon(android.R.drawable.ic_menu_close_clear_cancel); + disconnect.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + // disconnect or close the currently visible session + TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip); + TerminalBridge bridge = terminalView.bridge; + bridge.dispatchDisconnect(true); + return true; + } + }); + copy = menu.add(R.string.console_menu_copy); + + if (hardKeyboard) + copy.setAlphabeticShortcut('c'); + + copy.setIcon(android.R.drawable.ic_menu_set_as); + copy.setEnabled(activeTerminal); + copy.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + // mark as copying and reset any previous bounds + TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip); + 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(); + 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) + 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) { + // force insert of clipboard text into current console + TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip); + TerminalBridge bridge = terminalView.bridge; + // pull string from clipboard and generate all events to force down + String clip = clipboard.getText().toString(); + bridge.injectString(clip); + return true; + } + }); + resize = menu.add(R.string.console_menu_resize); + + if (hardKeyboard) + resize.setAlphabeticShortcut('r'); + + resize.setIcon(android.R.drawable.ic_menu_crop); + resize.setEnabled(sessionOpen); + resize.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + final TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip); + final View resizeView = inflater.inflate(R.layout.dia_resize, null, false); + ((EditText) resizeView.findViewById(R.id.width)) + .setText(prefs.getString(PreferenceConstants.DEFAULT_FONT_SIZE_WIDTH, "80")); + ((EditText) resizeView.findViewById(R.id.height)) + .setText(prefs.getString(PreferenceConstants.DEFAULT_FONT_SIZE_HEIGHT, "25")); + new AlertDialog.Builder(ConsoleActivity.this) + .setView(resizeView) + .setPositiveButton(R.string.button_resize, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + int width, height; + + try { + width = Integer.parseInt(((EditText) resizeView + .findViewById(R.id.width)) + .getText().toString()); + height = Integer.parseInt(((EditText) resizeView + .findViewById(R.id.height)) + .getText().toString()); + } + catch (NumberFormatException nfe) { + // TODO change this to a real dialog where we can + // make the input boxes turn red to indicate an error. + return; + } + + if (width > 0 && height > 0) { + terminalView.forceSize(width, height); + } + else { + new AlertDialog.Builder(ConsoleActivity.this) + .setTitle(R.string.resize_error_title) + .setMessage(R.string.resize_error_width_height) + .setNegativeButton(R.string.button_close, null) + .show(); + } + } + }).setNeutralButton(R.string.button_resize_reset, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + terminalView.bridge.resetSize(terminalView); + } + }).setNegativeButton(android.R.string.cancel, null) + .create().show(); + return true; + } + }); + screenCapture = menu.add(R.string.console_menu_screencapture); + + if (hardKeyboard) + screenCapture.setAlphabeticShortcut('s'); + + screenCapture.setIcon(android.R.drawable.ic_menu_camera); + screenCapture.setEnabled(activeTerminal); + screenCapture.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + final TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip); + terminalView.bridge.captureScreen(); + return true; + } + }); + portForward = menu.add(R.string.console_menu_portforwards); + + if (hardKeyboard) + portForward.setAlphabeticShortcut('f'); + + portForward.setIcon(android.R.drawable.ic_menu_manage); + portForward.setEnabled(sessionOpen && canForwardPorts); + portForward.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip); + TerminalBridge bridge = terminalView.bridge; + Intent intent = new Intent(ConsoleActivity.this, PortForwardListActivity.class); + intent.putExtra(Intent.EXTRA_TITLE, bridge.host.getId()); + ConsoleActivity.this.startActivityForResult(intent, REQUEST_EDIT); + return true; + } + }); + urlscan = menu.add(R.string.console_menu_urlscan); + + if (hardKeyboard) + urlscan.setAlphabeticShortcut('l'); + + urlscan.setIcon(android.R.drawable.ic_menu_search); + urlscan.setEnabled(activeTerminal); + urlscan.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + View flip = findCurrentView(R.id.console_flip); + + if (flip == null) return true; + + TerminalView terminal = (TerminalView)flip; + TerminalKeyListener handler = terminal.bridge.getKeyHandler(); + handler.urlScan(terminal); + return true; + } + }); + download = menu.add(R.string.console_menu_download); + download.setAlphabeticShortcut('d'); + download.setEnabled(sessionOpen && canTransferFiles); + download.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + final String downloadFolder = prefs.getString(PreferenceConstants.DOWNLOAD_FOLDER, ""); + final TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip); + final TerminalBridge bridge = terminalView.bridge; + final EditText textField = new EditText(ConsoleActivity.this); + new AlertDialog.Builder(ConsoleActivity.this) + .setTitle(R.string.transfer_select_remote_download_title) + .setMessage(R.string.transfer_select_remote_download_desc) + .setView(textField) + .setPositiveButton(R.string.transfer_button_download, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + TransferThread transfer = new TransferThread(ConsoleActivity.this, handler); + + if (!prefs.getBoolean(PreferenceConstants.BACKGROUND_FILE_TRANSFER, true)) + transfer.setProgressDialogMessage(getString(R.string.transfer_downloading)); + + transfer.download(bridge, textField.getText().toString(), null, downloadFolder); + } + }).setNegativeButton(android.R.string.cancel, null).create().show(); + return true; + } + }); + upload = menu.add(R.string.console_menu_upload); + upload.setAlphabeticShortcut('u'); + upload.setEnabled(sessionOpen && canTransferFiles); + upload.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + FileChooser.selectFile(ConsoleActivity.this, ConsoleActivity.this, + FileChooser.REQUEST_CODE_SELECT_FILE, + getString(R.string.file_chooser_select_file, getString(R.string.select_for_upload))); + return true; + } + }); + return true; + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent intent) { + super.onActivityResult(requestCode, resultCode, intent); + + switch (requestCode) { + case FileChooser.REQUEST_CODE_SELECT_FILE: + if (resultCode == RESULT_OK && intent != null) { + Uri uri = intent.getData(); + + try { + if (uri != null) { + fileSelected(new File(URI.create(uri.toString()))); + } + else { + String filename = intent.getDataString(); + + if (filename != null) { + fileSelected(new File(URI.create(filename))); + } + } + } + catch (IllegalArgumentException e) { + Log.e(TAG, "Couldn't read from selected file", e); + } + } + + break; + } + } + + public void fileSelected(final File f) { + String destFileName; + String uploadFolder = prefs.getString(PreferenceConstants.REMOTE_UPLOAD_FOLDER, null); + final TransferThread transfer = new TransferThread(ConsoleActivity.this, handler); + Log.d(TAG, "File chooser returned " + f); + + if (uploadFolder == null) + uploadFolder = ""; + + if (!uploadFolder.equals("") && uploadFolder.charAt(uploadFolder.length() - 1) != '/') + destFileName = uploadFolder + "/" + f.getName(); + else + destFileName = uploadFolder + f.getName(); + + if (prefs.getBoolean(PreferenceConstants.UPLOAD_DESTINATION_PROMPT, true)) { + final EditText fileDest = new EditText(ConsoleActivity.this); + fileDest.setSingleLine(); + fileDest.setText(destFileName); + new AlertDialog.Builder(ConsoleActivity.this) + .setTitle(R.string.transfer_select_remote_upload_dest_title) + .setMessage(getResources().getString(R.string.transfer_select_remote_upload_dest_desc) + "\n" + f.getPath()) + .setView(fileDest) + .setPositiveButton(R.string.transfer_button_upload, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + if (!prefs.getBoolean(PreferenceConstants.BACKGROUND_FILE_TRANSFER, true)) + transfer.setProgressDialogMessage(getString(R.string.transfer_uploading)); + + TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip); + TerminalBridge bridge = terminalView.bridge; + File uf = new File(fileDest.getText().toString()); + String name = "", parent = ""; + + if (uf.getParent() != null) + parent = uf.getParent().toString(); + + if (uf.getName() != null) + name = uf.getName().toString(); + + transfer.upload(bridge, f.toString(), name, parent); + } + }).setNegativeButton(android.R.string.cancel, null).create().show(); + } + else { + if (!prefs.getBoolean(PreferenceConstants.BACKGROUND_FILE_TRANSFER, true)) + transfer.setProgressDialogMessage(getString(R.string.transfer_uploading)); + + TerminalView terminalView = (TerminalView) findCurrentView(R.id.console_flip); + TerminalBridge bridge = terminalView.bridge; + transfer.upload(bridge, f.toString(), null, uploadFolder); + } + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + setVolumeControlStream(AudioManager.STREAM_NOTIFICATION); + final View view = findCurrentView(R.id.console_flip); + boolean activeTerminal = (view instanceof TerminalView); + boolean sessionOpen = false; + boolean disconnected = false; + boolean canForwardPorts = false; + boolean canTransferFiles = false; + + if (activeTerminal) { + TerminalBridge bridge = ((TerminalView) view).bridge; + sessionOpen = bridge.isSessionOpen(); + disconnected = bridge.isDisconnected(); + canForwardPorts = bridge.canFowardPorts(); + canTransferFiles = bridge.canTransferFiles(); + } + + disconnect.setEnabled(activeTerminal); + + if (sessionOpen || !disconnected) + disconnect.setTitle(R.string.list_host_disconnect); + else + disconnect.setTitle(R.string.console_menu_close); + + copy.setEnabled(activeTerminal); + paste.setEnabled(clipboard.hasText() && sessionOpen); + portForward.setEnabled(sessionOpen && canForwardPorts); + urlscan.setEnabled(activeTerminal); + resize.setEnabled(sessionOpen); + download.setEnabled(sessionOpen && canTransferFiles); + upload.setEnabled(sessionOpen && canTransferFiles); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + Intent intent = new Intent(this, HostListActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + public void onOptionsMenuClosed(Menu menu) { + super.onOptionsMenuClosed(menu); + setVolumeControlStream(AudioManager.STREAM_MUSIC); + } + + @Override + public void onStart() { + super.onStart(); + // connect with manager service to find all bridges + // when connected it will insert all views + bindService(new Intent(this, TerminalManager.class), connection, Context.BIND_AUTO_CREATE); + + if (getResources().getConfiguration().hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) { + this.mKeyboardButton.setVisibility(View.GONE); + } + } + + @Override + public void onPause() { + super.onPause(); + Log.d(TAG, "onPause called"); + + if (forcedOrientation && bound != null) + bound.setResizeAllowed(false); + } + + @Override + public void onResume() { + super.onResume(); + Log.d(TAG, "onResume called"); + + // Make sure we don't let the screen fall asleep. + // This also keeps the Wi-Fi chipset from disconnecting us. + if (prefs.getBoolean(PreferenceConstants.KEEP_ALIVE, true)) { + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + else { + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + + configureOrientation(); + + if (forcedOrientation && bound != null) + bound.setResizeAllowed(true); + } + + /* (non-Javadoc) + * @see android.app.Activity#onNewIntent(android.content.Intent) + */ + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + Log.d(TAG, "onNewIntent called"); + requested = intent.getData(); + + if (requested == null) { + Log.e(TAG, "Got null intent data in onNewIntent()"); + return; + } + + if (bound == null) { + Log.e(TAG, "We're not bound in onNewIntent()"); + return; + } + + TerminalBridge requestedBridge = bound.getConnectedBridge(requested.getFragment()); + int requestedIndex = 0; + + synchronized (flip) { + if (requestedBridge == null) { + // If we didn't find the requested connection, try opening it + try { + Log.d(TAG, String.format("We couldnt find an existing bridge with URI=%s (nickname=%s)," + + "so creating one now", requested.toString(), requested.getFragment())); + requestedBridge = bound.openConnection(requested); + } + catch (Exception e) { + Log.e(TAG, "Problem while trying to create new requested bridge from URI", e); + // TODO: We should display an error dialog here. + return; + } + + requestedIndex = addNewTerminalView(requestedBridge); + } + else { + final int flipIndex = getFlipIndex(requestedBridge); + + if (flipIndex > requestedIndex) { + requestedIndex = flipIndex; + } + } + + setDisplayedTerminal(requestedIndex); + } + } + + @Override + public void onStop() { + super.onStop(); + unbindService(connection); + } + + 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(); + } + } + + 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 + * later. + */ + private void updateDefault() { + // update the current default terminal + View view = findCurrentView(R.id.console_flip); + + if (!(view instanceof TerminalView)) return; + + TerminalView terminal = (TerminalView)view; + + if (bound != null) bound.defaultBridge = terminal.bridge; + + // tell the bridge monitor it has the topmost visible window now. + if (terminal.bridge.monitor != null) terminal.bridge.monitor.activate(); + } + + protected void updateEmptyVisible() { + // update visibility of empty status message + empty.setVisibility((flip.getChildCount() == 0) ? View.VISIBLE : View.GONE); + } + + /** + * Show any prompts requested by the currently visible {@link TerminalView}. + */ + protected void updatePromptVisible() { + // check if our currently-visible terminalbridge is requesting any prompt services + View view = findCurrentView(R.id.console_flip); + // Hide all the prompts in case a prompt request was canceled + hideAllPrompts(); + + if (!(view instanceof TerminalView)) { + // we dont have an active view, so hide any prompts + return; + } + + PromptHelper prompt = ((TerminalView)view).bridge.promptHelper; + + if (String.class.equals(prompt.promptRequested)) { + stringPromptGroup.setVisibility(View.VISIBLE); + String instructions = prompt.promptInstructions; + boolean password = prompt.passwordRequested; + + if (instructions != null && instructions.length() > 0) { + stringPromptInstructions.setVisibility(View.VISIBLE); + stringPromptInstructions.setText(instructions); + } + else + stringPromptInstructions.setVisibility(View.GONE); + + if (password) { + stringPrompt.setInputType(InputType.TYPE_CLASS_TEXT | + InputType.TYPE_TEXT_VARIATION_PASSWORD); + stringPrompt.setTransformationMethod(PasswordTransformationMethod.getInstance()); + } + else { + stringPrompt.setInputType(InputType.TYPE_CLASS_TEXT | + InputType.TYPE_TEXT_FLAG_AUTO_CORRECT); + stringPrompt.setTransformationMethod(SingleLineTransformationMethod.getInstance()); + } + + stringPrompt.setText(""); + stringPrompt.setHint(prompt.promptHint); + stringPrompt.requestFocus(); + } + else if (Boolean.class.equals(prompt.promptRequested)) { + booleanPromptGroup.setVisibility(View.VISIBLE); + booleanPrompt.setText(prompt.promptHint); + booleanYes.requestFocus(); + } + else { + hideAllPrompts(); + view.requestFocus(); + } + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + Log.d(TAG, String.format("onConfigurationChanged; requestedOrientation=%d, newConfig.orientation=%d", getRequestedOrientation(), newConfig.orientation)); + + if (bound != null) { + if (forcedOrientation && + (newConfig.orientation != Configuration.ORIENTATION_LANDSCAPE && + getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) || + (newConfig.orientation != Configuration.ORIENTATION_PORTRAIT && + getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)) + bound.setResizeAllowed(false); + else + bound.setResizeAllowed(true); + + bound.hardKeyboardHidden = (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES); + mKeyboardButton.setVisibility(bound.hardKeyboardHidden ? View.VISIBLE : View.GONE); + } + } + + /** + * 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; + } + } + + 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); + + if (view == null || !(view instanceof TerminalView)) { + // How did that happen? + continue; + } + + final TerminalView tv = (TerminalView) view; + + if (tv.bridge == bridge) { + return i; + } + } + } + + return -1; + } + + /** + * 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); + } + + updatePromptVisible(); + updateEmptyVisible(); + } + } + + private void setFullScreen(int fullScreen) { + if (fullScreen != this.fullScreen) { + if (fullScreen == FULLSCREEN_ON) { + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + else { + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + + this.fullScreen = fullScreen; + + if (bound != null) + bound.setFullScreen(this.fullScreen); + } + } +}