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