1 /* 2 * ConnectBot: simple, powerful, open-source SSH client for Android 3 * Copyright 2007 Kenny Root, Jeffrey Sharkey 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.connectbot.service; 19 20 import android.content.Context; 21 import android.content.SharedPreferences; 22 import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 23 import android.graphics.Bitmap; 24 import android.graphics.Canvas; 25 import android.graphics.Color; 26 import android.graphics.Paint; 27 import android.graphics.Typeface; 28 import android.graphics.Bitmap.Config; 29 import android.graphics.Paint.FontMetrics; 30 import android.text.ClipboardManager; 31 import android.view.ContextMenu; 32 import android.view.Menu; 33 import android.view.View; 34 import android.view.ContextMenu.ContextMenuInfo; 35 36 import com.googlecode.android_scripting.Log; 37 import com.googlecode.android_scripting.R; 38 import com.googlecode.android_scripting.facade.ui.UiFacade; 39 import com.googlecode.android_scripting.interpreter.InterpreterProcess; 40 import com.googlecode.android_scripting.jsonrpc.RpcReceiverManager; 41 import com.googlecode.android_scripting.jsonrpc.RpcReceiverManagerFactory; 42 43 import de.mud.terminal.VDUBuffer; 44 import de.mud.terminal.VDUDisplay; 45 import de.mud.terminal.vt320; 46 47 import java.io.IOException; 48 import java.nio.charset.Charset; 49 import java.util.LinkedList; 50 import java.util.List; 51 52 import org.connectbot.TerminalView; 53 import org.connectbot.transport.AbsTransport; 54 import org.connectbot.util.Colors; 55 import org.connectbot.util.PreferenceConstants; 56 import org.connectbot.util.SelectionArea; 57 58 /** 59 * Provides a bridge between a MUD terminal buffer and a possible TerminalView. This separation 60 * allows us to keep the TerminalBridge running in a background service. A TerminalView shares down 61 * a bitmap that we can use for rendering when available. 62 * 63 * @author ConnectBot Dev Team 64 * @author raaar 65 * 66 */ 67 public class TerminalBridge implements VDUDisplay, OnSharedPreferenceChangeListener { 68 69 private final static int FONT_SIZE_STEP = 2; 70 71 private final int[] color = new int[Colors.defaults.length]; 72 73 private final TerminalManager manager; 74 75 private final InterpreterProcess mProcess; 76 77 private int mDefaultFgColor; 78 private int mDefaultBgColor; 79 80 private int scrollback; 81 82 private String delKey; 83 private String encoding; 84 85 private AbsTransport transport; 86 87 private final Paint defaultPaint; 88 89 private Relay relay; 90 91 private Bitmap bitmap = null; 92 private final VDUBuffer buffer; 93 94 private TerminalView parent = null; 95 private final Canvas canvas = new Canvas(); 96 97 private boolean forcedSize = false; 98 private int columns; 99 private int rows; 100 101 private final TerminalKeyListener keyListener; 102 103 private boolean selectingForCopy = false; 104 private final SelectionArea selectionArea; 105 private ClipboardManager clipboard; 106 107 public int charWidth = -1; 108 public int charHeight = -1; 109 private int charTop = -1; 110 111 private float fontSize = -1; 112 113 private final List<FontSizeChangedListener> fontSizeChangedListeners; 114 115 /** 116 * Flag indicating if we should perform a full-screen redraw during our next rendering pass. 117 */ 118 private boolean fullRedraw = false; 119 120 private final PromptHelper promptHelper; 121 122 /** 123 * Create a new terminal bridge suitable for unit testing. 124 */ 125 public TerminalBridge() { 126 buffer = new vt320() { 127 @Override 128 public void write(byte[] b) { 129 } 130 131 @Override 132 public void write(int b) { 133 } 134 135 @Override 136 public void sendTelnetCommand(byte cmd) { 137 } 138 139 @Override 140 public void setWindowSize(int c, int r) { 141 } 142 143 @Override 144 public void debug(String s) { 145 } 146 }; 147 148 manager = null; 149 150 defaultPaint = new Paint(); 151 152 selectionArea = new SelectionArea(); 153 scrollback = 1; 154 155 fontSizeChangedListeners = new LinkedList<FontSizeChangedListener>(); 156 157 transport = null; 158 159 keyListener = new TerminalKeyListener(manager, this, buffer, null); 160 161 mProcess = null; 162 163 mDefaultFgColor = 0; 164 mDefaultBgColor = 0; 165 promptHelper = null; 166 167 updateCharset(); 168 } 169 170 /** 171 * Create new terminal bridge with following parameters. 172 */ 173 public TerminalBridge(final TerminalManager manager, InterpreterProcess process, AbsTransport t) 174 throws IOException { 175 this.manager = manager; 176 transport = t; 177 mProcess = process; 178 179 String string = manager.getStringParameter(PreferenceConstants.SCROLLBACK, null); 180 if (string != null) { 181 scrollback = Integer.parseInt(string); 182 } else { 183 scrollback = PreferenceConstants.DEFAULT_SCROLLBACK; 184 } 185 186 string = manager.getStringParameter(PreferenceConstants.FONTSIZE, null); 187 if (string != null) { 188 fontSize = Float.parseFloat(string); 189 } else { 190 fontSize = PreferenceConstants.DEFAULT_FONT_SIZE; 191 } 192 193 mDefaultFgColor = 194 manager.getIntParameter(PreferenceConstants.COLOR_FG, PreferenceConstants.DEFAULT_FG_COLOR); 195 mDefaultBgColor = 196 manager.getIntParameter(PreferenceConstants.COLOR_BG, PreferenceConstants.DEFAULT_BG_COLOR); 197 198 delKey = manager.getStringParameter(PreferenceConstants.DELKEY, PreferenceConstants.DELKEY_DEL); 199 200 // create prompt helper to relay password and hostkey requests up to gui 201 promptHelper = new PromptHelper(this); 202 203 // create our default paint 204 defaultPaint = new Paint(); 205 defaultPaint.setAntiAlias(true); 206 defaultPaint.setTypeface(Typeface.MONOSPACE); 207 defaultPaint.setFakeBoldText(true); // more readable? 208 209 fontSizeChangedListeners = new LinkedList<FontSizeChangedListener>(); 210 211 setFontSize(fontSize); 212 213 // create terminal buffer and handle outgoing data 214 // this is probably status reply information 215 buffer = new vt320() { 216 @Override 217 public void debug(String s) { 218 Log.d(s); 219 } 220 221 @Override 222 public void write(byte[] b) { 223 try { 224 if (b != null && transport != null) { 225 transport.write(b); 226 } 227 } catch (IOException e) { 228 Log.e("Problem writing outgoing data in vt320() thread", e); 229 } 230 } 231 232 @Override 233 public void write(int b) { 234 try { 235 if (transport != null) { 236 transport.write(b); 237 } 238 } catch (IOException e) { 239 Log.e("Problem writing outgoing data in vt320() thread", e); 240 } 241 } 242 243 // We don't use telnet sequences. 244 @Override 245 public void sendTelnetCommand(byte cmd) { 246 } 247 248 // We don't want remote to resize our window. 249 @Override 250 public void setWindowSize(int c, int r) { 251 } 252 253 @Override 254 public void beep() { 255 if (parent.isShown()) { 256 manager.playBeep(); 257 } 258 } 259 }; 260 261 // Don't keep any scrollback if a session is not being opened. 262 263 buffer.setBufferSize(scrollback); 264 265 resetColors(); 266 buffer.setDisplay(this); 267 268 selectionArea = new SelectionArea(); 269 270 keyListener = new TerminalKeyListener(manager, this, buffer, encoding); 271 272 updateCharset(); 273 274 manager.registerOnSharedPreferenceChangeListener(this); 275 276 } 277 278 /** 279 * Spawn thread to open connection and start login process. 280 */ 281 protected void connect() { 282 transport.setBridge(this); 283 transport.setManager(manager); 284 transport.connect(); 285 286 ((vt320) buffer).reset(); 287 288 // previously tried vt100 and xterm for emulation modes 289 // "screen" works the best for color and escape codes 290 ((vt320) buffer).setAnswerBack("screen"); 291 292 if (PreferenceConstants.DELKEY_BACKSPACE.equals(delKey)) { 293 ((vt320) buffer).setBackspace(vt320.DELETE_IS_BACKSPACE); 294 } else { 295 ((vt320) buffer).setBackspace(vt320.DELETE_IS_DEL); 296 } 297 298 // create thread to relay incoming connection data to buffer 299 relay = new Relay(this, transport, (vt320) buffer, encoding); 300 Thread relayThread = new Thread(relay); 301 relayThread.setDaemon(true); 302 relayThread.setName("Relay"); 303 relayThread.start(); 304 305 // force font-size to make sure we resizePTY as needed 306 setFontSize(fontSize); 307 308 } 309 310 private void updateCharset() { 311 encoding = 312 manager.getStringParameter(PreferenceConstants.ENCODING, Charset.defaultCharset().name()); 313 if (relay != null) { 314 relay.setCharset(encoding); 315 } 316 keyListener.setCharset(encoding); 317 } 318 319 /** 320 * Inject a specific string into this terminal. Used for post-login strings and pasting clipboard. 321 */ 322 public void injectString(final String string) { 323 if (string == null || string.length() == 0) { 324 return; 325 } 326 327 Thread injectStringThread = new Thread(new Runnable() { 328 public void run() { 329 try { 330 transport.write(string.getBytes(encoding)); 331 } catch (Exception e) { 332 Log.e("Couldn't inject string to remote host: ", e); 333 } 334 } 335 }); 336 injectStringThread.setName("InjectString"); 337 injectStringThread.start(); 338 } 339 340 /** 341 * @return whether a session is open or not 342 */ 343 public boolean isSessionOpen() { 344 if (transport != null) { 345 return transport.isSessionOpen(); 346 } 347 return false; 348 } 349 350 /** 351 * Force disconnection of this terminal bridge. 352 */ 353 public void dispatchDisconnect(boolean immediate) { 354 355 // Cancel any pending prompts. 356 promptHelper.cancelPrompt(); 357 358 if (immediate) { 359 manager.closeConnection(TerminalBridge.this, true); 360 } else { 361 Thread disconnectPromptThread = new Thread(new Runnable() { 362 public void run() { 363 String prompt = null; 364 if (transport != null && transport.isConnected()) { 365 prompt = manager.getResources().getString(R.string.prompt_confirm_exit); 366 } else { 367 prompt = manager.getResources().getString(R.string.prompt_process_exited); 368 } 369 Boolean result = promptHelper.requestBooleanPrompt(null, prompt); 370 371 if (transport != null && transport.isConnected()) { 372 manager.closeConnection(TerminalBridge.this, result != null && result.booleanValue()); 373 } else if (result != null && result.booleanValue()) { 374 manager.closeConnection(TerminalBridge.this, false); 375 } 376 } 377 }); 378 disconnectPromptThread.setName("DisconnectPrompt"); 379 disconnectPromptThread.setDaemon(true); 380 disconnectPromptThread.start(); 381 } 382 } 383 384 public void setSelectingForCopy(boolean selectingForCopy) { 385 this.selectingForCopy = selectingForCopy; 386 } 387 388 public boolean isSelectingForCopy() { 389 return selectingForCopy; 390 } 391 392 public SelectionArea getSelectionArea() { 393 return selectionArea; 394 } 395 396 public synchronized void tryKeyVibrate() { 397 manager.tryKeyVibrate(); 398 } 399 400 /** 401 * Request a different font size. Will make call to parentChanged() to make sure we resize PTY if 402 * needed. 403 */ 404 /* package */final void setFontSize(float size) { 405 if (size <= 0.0) { 406 return; 407 } 408 409 defaultPaint.setTextSize(size); 410 fontSize = size; 411 412 // read new metrics to get exact pixel dimensions 413 FontMetrics fm = defaultPaint.getFontMetrics(); 414 charTop = (int) Math.ceil(fm.top); 415 416 float[] widths = new float[1]; 417 defaultPaint.getTextWidths("X", widths); 418 charWidth = (int) Math.ceil(widths[0]); 419 charHeight = (int) Math.ceil(fm.descent - fm.top); 420 421 // refresh any bitmap with new font size 422 if (parent != null) { 423 parentChanged(parent); 424 } 425 426 for (FontSizeChangedListener ofscl : fontSizeChangedListeners) { 427 ofscl.onFontSizeChanged(size); 428 } 429 forcedSize = false; 430 } 431 432 /** 433 * Add an {@link FontSizeChangedListener} to the list of listeners for this bridge. 434 * 435 * @param listener 436 * listener to add 437 */ 438 public void addFontSizeChangedListener(FontSizeChangedListener listener) { 439 fontSizeChangedListeners.add(listener); 440 } 441 442 /** 443 * Remove an {@link FontSizeChangedListener} from the list of listeners for this bridge. 444 * 445 * @param listener 446 */ 447 public void removeFontSizeChangedListener(FontSizeChangedListener listener) { 448 fontSizeChangedListeners.remove(listener); 449 } 450 451 /** 452 * Something changed in our parent {@link TerminalView}, maybe it's a new parent, or maybe it's an 453 * updated font size. We should recalculate terminal size information and request a PTY resize. 454 */ 455 public final synchronized void parentChanged(TerminalView parent) { 456 if (manager != null && !manager.isResizeAllowed()) { 457 Log.d("Resize is not allowed now"); 458 return; 459 } 460 461 this.parent = parent; 462 final int width = parent.getWidth(); 463 final int height = parent.getHeight(); 464 465 // Something has gone wrong with our layout; we're 0 width or height! 466 if (width <= 0 || height <= 0) { 467 return; 468 } 469 470 clipboard = (ClipboardManager) parent.getContext().getSystemService(Context.CLIPBOARD_SERVICE); 471 keyListener.setClipboardManager(clipboard); 472 473 if (!forcedSize) { 474 // recalculate buffer size 475 int newColumns, newRows; 476 477 newColumns = width / charWidth; 478 newRows = height / charHeight; 479 480 // If nothing has changed in the terminal dimensions and not an intial 481 // draw then don't blow away scroll regions and such. 482 if (newColumns == columns && newRows == rows) { 483 return; 484 } 485 486 columns = newColumns; 487 rows = newRows; 488 } 489 490 // reallocate new bitmap if needed 491 boolean newBitmap = (bitmap == null); 492 if (bitmap != null) { 493 newBitmap = (bitmap.getWidth() != width || bitmap.getHeight() != height); 494 } 495 496 if (newBitmap) { 497 discardBitmap(); 498 bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888); 499 canvas.setBitmap(bitmap); 500 } 501 502 // clear out any old buffer information 503 defaultPaint.setColor(Color.BLACK); 504 canvas.drawPaint(defaultPaint); 505 506 // Stroke the border of the terminal if the size is being forced; 507 if (forcedSize) { 508 int borderX = (columns * charWidth) + 1; 509 int borderY = (rows * charHeight) + 1; 510 511 defaultPaint.setColor(Color.GRAY); 512 defaultPaint.setStrokeWidth(0.0f); 513 if (width >= borderX) { 514 canvas.drawLine(borderX, 0, borderX, borderY + 1, defaultPaint); 515 } 516 if (height >= borderY) { 517 canvas.drawLine(0, borderY, borderX + 1, borderY, defaultPaint); 518 } 519 } 520 521 try { 522 // request a terminal pty resize 523 synchronized (buffer) { 524 buffer.setScreenSize(columns, rows, true); 525 } 526 527 if (transport != null) { 528 transport.setDimensions(columns, rows, width, height); 529 } 530 } catch (Exception e) { 531 Log.e("Problem while trying to resize screen or PTY", e); 532 } 533 534 // force full redraw with new buffer size 535 fullRedraw = true; 536 redraw(); 537 538 parent.notifyUser(String.format("%d x %d", columns, rows)); 539 540 Log.i(String.format("parentChanged() now width=%d, height=%d", columns, rows)); 541 } 542 543 /** 544 * Somehow our parent {@link TerminalView} was destroyed. Now we don't need to redraw anywhere, 545 * and we can recycle our internal bitmap. 546 */ 547 public synchronized void parentDestroyed() { 548 parent = null; 549 discardBitmap(); 550 } 551 552 private void discardBitmap() { 553 if (bitmap != null) { 554 bitmap.recycle(); 555 } 556 bitmap = null; 557 } 558 559 public void onDraw() { 560 int fg, bg; 561 synchronized (buffer) { 562 boolean entireDirty = buffer.update[0] || fullRedraw; 563 boolean isWideCharacter = false; 564 565 // walk through all lines in the buffer 566 for (int l = 0; l < buffer.height; l++) { 567 568 // check if this line is dirty and needs to be repainted 569 // also check for entire-buffer dirty flags 570 if (!entireDirty && !buffer.update[l + 1]) { 571 continue; 572 } 573 574 // reset dirty flag for this line 575 buffer.update[l + 1] = false; 576 577 // walk through all characters in this line 578 for (int c = 0; c < buffer.width; c++) { 579 int addr = 0; 580 int currAttr = buffer.charAttributes[buffer.windowBase + l][c]; 581 // check if foreground color attribute is set 582 if ((currAttr & VDUBuffer.COLOR_FG) != 0) { 583 int fgcolor = ((currAttr & VDUBuffer.COLOR_FG) >> VDUBuffer.COLOR_FG_SHIFT) - 1; 584 if (fgcolor < 8 && (currAttr & VDUBuffer.BOLD) != 0) { 585 fg = color[fgcolor + 8]; 586 } else { 587 fg = color[fgcolor]; 588 } 589 } else { 590 fg = mDefaultFgColor; 591 } 592 593 // check if background color attribute is set 594 if ((currAttr & VDUBuffer.COLOR_BG) != 0) { 595 bg = color[((currAttr & VDUBuffer.COLOR_BG) >> VDUBuffer.COLOR_BG_SHIFT) - 1]; 596 } else { 597 bg = mDefaultBgColor; 598 } 599 600 // support character inversion by swapping background and foreground color 601 if ((currAttr & VDUBuffer.INVERT) != 0) { 602 int swapc = bg; 603 bg = fg; 604 fg = swapc; 605 } 606 607 // set underlined attributes if requested 608 defaultPaint.setUnderlineText((currAttr & VDUBuffer.UNDERLINE) != 0); 609 610 isWideCharacter = (currAttr & VDUBuffer.FULLWIDTH) != 0; 611 612 if (isWideCharacter) { 613 addr++; 614 } else { 615 // determine the amount of continuous characters with the same settings and print them 616 // all at once 617 while (c + addr < buffer.width 618 && buffer.charAttributes[buffer.windowBase + l][c + addr] == currAttr) { 619 addr++; 620 } 621 } 622 623 // Save the current clip region 624 canvas.save(Canvas.CLIP_SAVE_FLAG); 625 626 // clear this dirty area with background color 627 defaultPaint.setColor(bg); 628 if (isWideCharacter) { 629 canvas.clipRect(c * charWidth, l * charHeight, (c + 2) * charWidth, (l + 1) 630 * charHeight); 631 } else { 632 canvas.clipRect(c * charWidth, l * charHeight, (c + addr) * charWidth, (l + 1) 633 * charHeight); 634 } 635 canvas.drawPaint(defaultPaint); 636 637 // write the text string starting at 'c' for 'addr' number of characters 638 defaultPaint.setColor(fg); 639 if ((currAttr & VDUBuffer.INVISIBLE) == 0) { 640 canvas.drawText(buffer.charArray[buffer.windowBase + l], c, addr, c * charWidth, 641 (l * charHeight) - charTop, defaultPaint); 642 } 643 644 // Restore the previous clip region 645 canvas.restore(); 646 647 // advance to the next text block with different characteristics 648 c += addr - 1; 649 if (isWideCharacter) { 650 c++; 651 } 652 } 653 } 654 655 // reset entire-buffer flags 656 buffer.update[0] = false; 657 } 658 fullRedraw = false; 659 } 660 661 public void redraw() { 662 if (parent != null) { 663 parent.postInvalidate(); 664 } 665 } 666 667 // We don't have a scroll bar. 668 public void updateScrollBar() { 669 } 670 671 /** 672 * Resize terminal to fit [rows]x[cols] in screen of size [width]x[height] 673 * 674 * @param rows 675 * @param cols 676 * @param width 677 * @param height 678 */ 679 public synchronized void resizeComputed(int cols, int rows, int width, int height) { 680 float size = 8.0f; 681 float step = 8.0f; 682 float limit = 0.125f; 683 684 int direction; 685 686 while ((direction = fontSizeCompare(size, cols, rows, width, height)) < 0) { 687 size += step; 688 } 689 690 if (direction == 0) { 691 Log.d(String.format("Fontsize: found match at %f", size)); 692 return; 693 } 694 695 step /= 2.0f; 696 size -= step; 697 698 while ((direction = fontSizeCompare(size, cols, rows, width, height)) != 0 && step >= limit) { 699 step /= 2.0f; 700 if (direction > 0) { 701 size -= step; 702 } else { 703 size += step; 704 } 705 } 706 707 if (direction > 0) { 708 size -= step; 709 } 710 711 columns = cols; 712 this.rows = rows; 713 setFontSize(size); 714 forcedSize = true; 715 } 716 717 private int fontSizeCompare(float size, int cols, int rows, int width, int height) { 718 // read new metrics to get exact pixel dimensions 719 defaultPaint.setTextSize(size); 720 FontMetrics fm = defaultPaint.getFontMetrics(); 721 722 float[] widths = new float[1]; 723 defaultPaint.getTextWidths("X", widths); 724 int termWidth = (int) widths[0] * cols; 725 int termHeight = (int) Math.ceil(fm.descent - fm.top) * rows; 726 727 Log.d(String.format("Fontsize: font size %f resulted in %d x %d", size, termWidth, termHeight)); 728 729 // Check to see if it fits in resolution specified. 730 if (termWidth > width || termHeight > height) { 731 return 1; 732 } 733 734 if (termWidth == width || termHeight == height) { 735 return 0; 736 } 737 738 return -1; 739 } 740 741 /* 742 * (non-Javadoc) 743 * 744 * @see de.mud.terminal.VDUDisplay#setVDUBuffer(de.mud.terminal.VDUBuffer) 745 */ 746 @Override 747 public void setVDUBuffer(VDUBuffer buffer) { 748 } 749 750 /* 751 * (non-Javadoc) 752 * 753 * @see de.mud.terminal.VDUDisplay#setColor(byte, byte, byte, byte) 754 */ 755 public void setColor(int index, int red, int green, int blue) { 756 // Don't allow the system colors to be overwritten for now. May violate specs. 757 if (index < color.length && index >= 16) { 758 color[index] = 0xff000000 | red << 16 | green << 8 | blue; 759 } 760 } 761 762 public final void resetColors() { 763 System.arraycopy(Colors.defaults, 0, color, 0, Colors.defaults.length); 764 } 765 766 public TerminalKeyListener getKeyHandler() { 767 return keyListener; 768 } 769 770 public void resetScrollPosition() { 771 // if we're in scrollback, scroll to bottom of window on input 772 if (buffer.windowBase != buffer.screenBase) { 773 buffer.setWindowBase(buffer.screenBase); 774 } 775 } 776 777 public void increaseFontSize() { 778 setFontSize(fontSize + FONT_SIZE_STEP); 779 } 780 781 public void decreaseFontSize() { 782 setFontSize(fontSize - FONT_SIZE_STEP); 783 } 784 785 public int getId() { 786 return mProcess.getPort(); 787 } 788 789 public String getName() { 790 return mProcess.getName(); 791 } 792 793 public InterpreterProcess getProcess() { 794 return mProcess; 795 } 796 797 public int getForegroundColor() { 798 return mDefaultFgColor; 799 } 800 801 public int getBackgroundColor() { 802 return mDefaultBgColor; 803 } 804 805 public VDUBuffer getVDUBuffer() { 806 return buffer; 807 } 808 809 public PromptHelper getPromptHelper() { 810 return promptHelper; 811 } 812 813 public Bitmap getBitmap() { 814 return bitmap; 815 } 816 817 public AbsTransport getTransport() { 818 return transport; 819 } 820 821 public Paint getPaint() { 822 return defaultPaint; 823 } 824 825 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { 826 if (mProcess.isAlive()) { 827 RpcReceiverManagerFactory rpcReceiverManagerFactory = mProcess.getRpcReceiverManagerFactory(); 828 for (RpcReceiverManager manager : rpcReceiverManagerFactory.getRpcReceiverManagers().values()) { 829 UiFacade facade = manager.getReceiver(UiFacade.class); 830 facade.onCreateContextMenu(menu, v, menuInfo); 831 } 832 } 833 } 834 835 public boolean onPrepareOptionsMenu(Menu menu) { 836 boolean returnValue = false; 837 if (mProcess.isAlive()) { 838 RpcReceiverManagerFactory rpcReceiverManagerFactory = mProcess.getRpcReceiverManagerFactory(); 839 for (RpcReceiverManager manager : rpcReceiverManagerFactory.getRpcReceiverManagers().values()) { 840 UiFacade facade = manager.getReceiver(UiFacade.class); 841 returnValue = returnValue || facade.onPrepareOptionsMenu(menu); 842 } 843 return returnValue; 844 } 845 return false; 846 } 847 848 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 849 if (PreferenceConstants.ENCODING.equals(key)) { 850 updateCharset(); 851 } else if (PreferenceConstants.FONTSIZE.equals(key)) { 852 String string = manager.getStringParameter(PreferenceConstants.FONTSIZE, null); 853 if (string != null) { 854 fontSize = Float.parseFloat(string); 855 } else { 856 fontSize = PreferenceConstants.DEFAULT_FONT_SIZE; 857 } 858 setFontSize(fontSize); 859 fullRedraw = true; 860 } else if (PreferenceConstants.SCROLLBACK.equals(key)) { 861 String string = manager.getStringParameter(PreferenceConstants.SCROLLBACK, null); 862 if (string != null) { 863 scrollback = Integer.parseInt(string); 864 } else { 865 scrollback = PreferenceConstants.DEFAULT_SCROLLBACK; 866 } 867 buffer.setBufferSize(scrollback); 868 } else if (PreferenceConstants.COLOR_FG.equals(key)) { 869 mDefaultFgColor = 870 manager.getIntParameter(PreferenceConstants.COLOR_FG, 871 PreferenceConstants.DEFAULT_FG_COLOR); 872 fullRedraw = true; 873 } else if (PreferenceConstants.COLOR_BG.equals(key)) { 874 mDefaultBgColor = 875 manager.getIntParameter(PreferenceConstants.COLOR_BG, 876 PreferenceConstants.DEFAULT_BG_COLOR); 877 fullRedraw = true; 878 } 879 if (PreferenceConstants.DELKEY.equals(key)) { 880 delKey = 881 manager.getStringParameter(PreferenceConstants.DELKEY, PreferenceConstants.DELKEY_DEL); 882 if (PreferenceConstants.DELKEY_BACKSPACE.equals(delKey)) { 883 ((vt320) buffer).setBackspace(vt320.DELETE_IS_BACKSPACE); 884 } else { 885 ((vt320) buffer).setBackspace(vt320.DELETE_IS_DEL); 886 } 887 } 888 } 889 } 890