1134545d2d8429615552931ef50a15ba2994d03f8Insun Kang/* 2134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * Copyright 2018 The Android Open Source Project 3134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * 4134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * Licensed under the Apache License, Version 2.0 (the "License"); 5134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * you may not use this file except in compliance with the License. 6134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * You may obtain a copy of the License at 7134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * 8134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * http://www.apache.org/licenses/LICENSE-2.0 9134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * 10134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * Unless required by applicable law or agreed to in writing, software 11134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * distributed under the License is distributed on an "AS IS" BASIS, 12134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * See the License for the specific language governing permissions and 14134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * limitations under the License. 15134545d2d8429615552931ef50a15ba2994d03f8Insun Kang */ 16134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 17134545d2d8429615552931ef50a15ba2994d03f8Insun Kangpackage androidx.media.subtitle; 18134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 19134545d2d8429615552931ef50a15ba2994d03f8Insun Kangimport android.text.Spannable; 20134545d2d8429615552931ef50a15ba2994d03f8Insun Kangimport android.text.SpannableStringBuilder; 21134545d2d8429615552931ef50a15ba2994d03f8Insun Kangimport android.text.TextPaint; 22134545d2d8429615552931ef50a15ba2994d03f8Insun Kangimport android.text.style.CharacterStyle; 23134545d2d8429615552931ef50a15ba2994d03f8Insun Kangimport android.text.style.StyleSpan; 24134545d2d8429615552931ef50a15ba2994d03f8Insun Kangimport android.text.style.UnderlineSpan; 25134545d2d8429615552931ef50a15ba2994d03f8Insun Kangimport android.text.style.UpdateAppearance; 26134545d2d8429615552931ef50a15ba2994d03f8Insun Kangimport android.util.Log; 27134545d2d8429615552931ef50a15ba2994d03f8Insun Kangimport android.view.accessibility.CaptioningManager.CaptionStyle; 28134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 29134545d2d8429615552931ef50a15ba2994d03f8Insun Kangimport java.util.ArrayList; 30134545d2d8429615552931ef50a15ba2994d03f8Insun Kangimport java.util.Arrays; 31134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 32134545d2d8429615552931ef50a15ba2994d03f8Insun Kang/** 33134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * CCParser processes CEA-608 closed caption data. 34134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * 35134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * It calls back into OnDisplayChangedListener upon 36134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * display change with styled text for rendering. 37134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * 38134545d2d8429615552931ef50a15ba2994d03f8Insun Kang */ 39134545d2d8429615552931ef50a15ba2994d03f8Insun Kangclass Cea608CCParser { 40134545d2d8429615552931ef50a15ba2994d03f8Insun Kang public static final int MAX_ROWS = 15; 41134545d2d8429615552931ef50a15ba2994d03f8Insun Kang public static final int MAX_COLS = 32; 42134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 43134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final String TAG = "Cea608CCParser"; 44134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 45134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 46134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int INVALID = -1; 47134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 48134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // EIA-CEA-608: Table 70 - Control Codes 49134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int RCL = 0x20; 50134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int BS = 0x21; 51134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int AOF = 0x22; 52134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int AON = 0x23; 53134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int DER = 0x24; 54134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int RU2 = 0x25; 55134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int RU3 = 0x26; 56134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int RU4 = 0x27; 57134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int FON = 0x28; 58134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int RDC = 0x29; 59134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int TR = 0x2a; 60134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int RTD = 0x2b; 61134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int EDM = 0x2c; 62134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int CR = 0x2d; 63134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int ENM = 0x2e; 64134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int EOC = 0x2f; 65134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 66134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // Transparent Space 67134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final char TS = '\u00A0'; 68134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 69134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // Captioning Modes 70134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int MODE_UNKNOWN = 0; 71134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int MODE_PAINT_ON = 1; 72134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int MODE_ROLL_UP = 2; 73134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int MODE_POP_ON = 3; 74134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final int MODE_TEXT = 4; 75134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 76134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private final DisplayListener mListener; 77134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 78134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private int mMode = MODE_PAINT_ON; 79134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private int mRollUpSize = 4; 80134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private int mPrevCtrlCode = INVALID; 81134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 82134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private CCMemory mDisplay = new CCMemory(); 83134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private CCMemory mNonDisplay = new CCMemory(); 84134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private CCMemory mTextMem = new CCMemory(); 85134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 86134545d2d8429615552931ef50a15ba2994d03f8Insun Kang Cea608CCParser(DisplayListener listener) { 87134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mListener = listener; 88134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 89134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 90134545d2d8429615552931ef50a15ba2994d03f8Insun Kang public void parse(byte[] data) { 91134545d2d8429615552931ef50a15ba2994d03f8Insun Kang CCData[] ccData = CCData.fromByteArray(data); 92134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 93134545d2d8429615552931ef50a15ba2994d03f8Insun Kang for (int i = 0; i < ccData.length; i++) { 94134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (DEBUG) { 95134545d2d8429615552931ef50a15ba2994d03f8Insun Kang Log.d(TAG, ccData[i].toString()); 96134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 97134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 98134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (handleCtrlCode(ccData[i]) 99134545d2d8429615552931ef50a15ba2994d03f8Insun Kang || handleTabOffsets(ccData[i]) 100134545d2d8429615552931ef50a15ba2994d03f8Insun Kang || handlePACCode(ccData[i]) 101134545d2d8429615552931ef50a15ba2994d03f8Insun Kang || handleMidRowCode(ccData[i])) { 102134545d2d8429615552931ef50a15ba2994d03f8Insun Kang continue; 103134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 104134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 105134545d2d8429615552931ef50a15ba2994d03f8Insun Kang handleDisplayableChars(ccData[i]); 106134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 107134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 108134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 109134545d2d8429615552931ef50a15ba2994d03f8Insun Kang interface DisplayListener { 110134545d2d8429615552931ef50a15ba2994d03f8Insun Kang void onDisplayChanged(SpannableStringBuilder[] styledTexts); 111134545d2d8429615552931ef50a15ba2994d03f8Insun Kang CaptionStyle getCaptionStyle(); 112134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 113134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 114134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private CCMemory getMemory() { 115134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // get the CC memory to operate on for current mode 116134545d2d8429615552931ef50a15ba2994d03f8Insun Kang switch (mMode) { 117134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case MODE_POP_ON: 118134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return mNonDisplay; 119134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case MODE_TEXT: 120134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // TODO(chz): support only caption mode for now, 121134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // in text mode, dump everything to text mem. 122134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return mTextMem; 123134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case MODE_PAINT_ON: 124134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case MODE_ROLL_UP: 125134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return mDisplay; 126134545d2d8429615552931ef50a15ba2994d03f8Insun Kang default: 127134545d2d8429615552931ef50a15ba2994d03f8Insun Kang Log.w(TAG, "unrecoginized mode: " + mMode); 128134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 129134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return mDisplay; 130134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 131134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 132134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private boolean handleDisplayableChars(CCData ccData) { 133134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (!ccData.isDisplayableChar()) { 134134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return false; 135134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 136134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 137134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // Extended char includes 1 automatic backspace 138134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (ccData.isExtendedChar()) { 139134545d2d8429615552931ef50a15ba2994d03f8Insun Kang getMemory().bs(); 140134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 141134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 142134545d2d8429615552931ef50a15ba2994d03f8Insun Kang getMemory().writeText(ccData.getDisplayText()); 143134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 144134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (mMode == MODE_PAINT_ON || mMode == MODE_ROLL_UP) { 145134545d2d8429615552931ef50a15ba2994d03f8Insun Kang updateDisplay(); 146134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 147134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 148134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return true; 149134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 150134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 151134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private boolean handleMidRowCode(CCData ccData) { 152134545d2d8429615552931ef50a15ba2994d03f8Insun Kang StyleCode m = ccData.getMidRow(); 153134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (m != null) { 154134545d2d8429615552931ef50a15ba2994d03f8Insun Kang getMemory().writeMidRowCode(m); 155134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return true; 156134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 157134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return false; 158134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 159134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 160134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private boolean handlePACCode(CCData ccData) { 161134545d2d8429615552931ef50a15ba2994d03f8Insun Kang PAC pac = ccData.getPAC(); 162134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 163134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (pac != null) { 164134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (mMode == MODE_ROLL_UP) { 165134545d2d8429615552931ef50a15ba2994d03f8Insun Kang getMemory().moveBaselineTo(pac.getRow(), mRollUpSize); 166134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 167134545d2d8429615552931ef50a15ba2994d03f8Insun Kang getMemory().writePAC(pac); 168134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return true; 169134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 170134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 171134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return false; 172134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 173134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 174134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private boolean handleTabOffsets(CCData ccData) { 175134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int tabs = ccData.getTabOffset(); 176134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 177134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (tabs > 0) { 178134545d2d8429615552931ef50a15ba2994d03f8Insun Kang getMemory().tab(tabs); 179134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return true; 180134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 181134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 182134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return false; 183134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 184134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 185134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private boolean handleCtrlCode(CCData ccData) { 186134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int ctrlCode = ccData.getCtrlCode(); 187134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 188134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (mPrevCtrlCode != INVALID && mPrevCtrlCode == ctrlCode) { 189134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // discard double ctrl codes (but if there's a 3rd one, we still take that) 190134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mPrevCtrlCode = INVALID; 191134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return true; 192134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 193134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 194134545d2d8429615552931ef50a15ba2994d03f8Insun Kang switch(ctrlCode) { 195134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case RCL: 196134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // select pop-on style 197134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mMode = MODE_POP_ON; 198134545d2d8429615552931ef50a15ba2994d03f8Insun Kang break; 199134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case BS: 200134545d2d8429615552931ef50a15ba2994d03f8Insun Kang getMemory().bs(); 201134545d2d8429615552931ef50a15ba2994d03f8Insun Kang break; 202134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case DER: 203134545d2d8429615552931ef50a15ba2994d03f8Insun Kang getMemory().der(); 204134545d2d8429615552931ef50a15ba2994d03f8Insun Kang break; 205134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case RU2: 206134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case RU3: 207134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case RU4: 208134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mRollUpSize = (ctrlCode - 0x23); 209134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // erase memory if currently in other style 210134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (mMode != MODE_ROLL_UP) { 211134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mDisplay.erase(); 212134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mNonDisplay.erase(); 213134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 214134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // select roll-up style 215134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mMode = MODE_ROLL_UP; 216134545d2d8429615552931ef50a15ba2994d03f8Insun Kang break; 217134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case FON: 218134545d2d8429615552931ef50a15ba2994d03f8Insun Kang Log.i(TAG, "Flash On"); 219134545d2d8429615552931ef50a15ba2994d03f8Insun Kang break; 220134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case RDC: 221134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // select paint-on style 222134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mMode = MODE_PAINT_ON; 223134545d2d8429615552931ef50a15ba2994d03f8Insun Kang break; 224134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case TR: 225134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mMode = MODE_TEXT; 226134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mTextMem.erase(); 227134545d2d8429615552931ef50a15ba2994d03f8Insun Kang break; 228134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case RTD: 229134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mMode = MODE_TEXT; 230134545d2d8429615552931ef50a15ba2994d03f8Insun Kang break; 231134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case EDM: 232134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // erase display memory 233134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mDisplay.erase(); 234134545d2d8429615552931ef50a15ba2994d03f8Insun Kang updateDisplay(); 235134545d2d8429615552931ef50a15ba2994d03f8Insun Kang break; 236134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case CR: 237134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (mMode == MODE_ROLL_UP) { 238134545d2d8429615552931ef50a15ba2994d03f8Insun Kang getMemory().rollUp(mRollUpSize); 239134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } else { 240134545d2d8429615552931ef50a15ba2994d03f8Insun Kang getMemory().cr(); 241134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 242134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (mMode == MODE_ROLL_UP) { 243134545d2d8429615552931ef50a15ba2994d03f8Insun Kang updateDisplay(); 244134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 245134545d2d8429615552931ef50a15ba2994d03f8Insun Kang break; 246134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case ENM: 247134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // erase non-display memory 248134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mNonDisplay.erase(); 249134545d2d8429615552931ef50a15ba2994d03f8Insun Kang break; 250134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case EOC: 251134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // swap display/non-display memory 252134545d2d8429615552931ef50a15ba2994d03f8Insun Kang swapMemory(); 253134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // switch to pop-on style 254134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mMode = MODE_POP_ON; 255134545d2d8429615552931ef50a15ba2994d03f8Insun Kang updateDisplay(); 256134545d2d8429615552931ef50a15ba2994d03f8Insun Kang break; 257134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case INVALID: 258134545d2d8429615552931ef50a15ba2994d03f8Insun Kang default: 259134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mPrevCtrlCode = INVALID; 260134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return false; 261134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 262134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 263134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mPrevCtrlCode = ctrlCode; 264134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 265134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // handled 266134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return true; 267134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 268134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 269134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private void updateDisplay() { 270134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (mListener != null) { 271134545d2d8429615552931ef50a15ba2994d03f8Insun Kang CaptionStyle captionStyle = mListener.getCaptionStyle(); 272134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mListener.onDisplayChanged(mDisplay.getStyledText(captionStyle)); 273134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 274134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 275134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 276134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private void swapMemory() { 277134545d2d8429615552931ef50a15ba2994d03f8Insun Kang CCMemory temp = mDisplay; 278134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mDisplay = mNonDisplay; 279134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mNonDisplay = temp; 280134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 281134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 282134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static class StyleCode { 283134545d2d8429615552931ef50a15ba2994d03f8Insun Kang static final int COLOR_WHITE = 0; 284134545d2d8429615552931ef50a15ba2994d03f8Insun Kang static final int COLOR_GREEN = 1; 285134545d2d8429615552931ef50a15ba2994d03f8Insun Kang static final int COLOR_BLUE = 2; 286134545d2d8429615552931ef50a15ba2994d03f8Insun Kang static final int COLOR_CYAN = 3; 287134545d2d8429615552931ef50a15ba2994d03f8Insun Kang static final int COLOR_RED = 4; 288134545d2d8429615552931ef50a15ba2994d03f8Insun Kang static final int COLOR_YELLOW = 5; 289134545d2d8429615552931ef50a15ba2994d03f8Insun Kang static final int COLOR_MAGENTA = 6; 290134545d2d8429615552931ef50a15ba2994d03f8Insun Kang static final int COLOR_INVALID = 7; 291134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 292134545d2d8429615552931ef50a15ba2994d03f8Insun Kang static final int STYLE_ITALICS = 0x00000001; 293134545d2d8429615552931ef50a15ba2994d03f8Insun Kang static final int STYLE_UNDERLINE = 0x00000002; 294134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 295134545d2d8429615552931ef50a15ba2994d03f8Insun Kang static final String[] sColorMap = { 296134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "WHITE", "GREEN", "BLUE", "CYAN", "RED", "YELLOW", "MAGENTA", "INVALID" 297134545d2d8429615552931ef50a15ba2994d03f8Insun Kang }; 298134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 299134545d2d8429615552931ef50a15ba2994d03f8Insun Kang final int mStyle; 300134545d2d8429615552931ef50a15ba2994d03f8Insun Kang final int mColor; 301134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 302134545d2d8429615552931ef50a15ba2994d03f8Insun Kang static StyleCode fromByte(byte data2) { 303134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int style = 0; 304134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int color = (data2 >> 1) & 0x7; 305134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 306134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if ((data2 & 0x1) != 0) { 307134545d2d8429615552931ef50a15ba2994d03f8Insun Kang style |= STYLE_UNDERLINE; 308134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 309134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 310134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (color == COLOR_INVALID) { 311134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // WHITE ITALICS 312134545d2d8429615552931ef50a15ba2994d03f8Insun Kang color = COLOR_WHITE; 313134545d2d8429615552931ef50a15ba2994d03f8Insun Kang style |= STYLE_ITALICS; 314134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 315134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 316134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return new StyleCode(style, color); 317134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 318134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 319134545d2d8429615552931ef50a15ba2994d03f8Insun Kang StyleCode(int style, int color) { 320134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mStyle = style; 321134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mColor = color; 322134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 323134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 324134545d2d8429615552931ef50a15ba2994d03f8Insun Kang boolean isItalics() { 325134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return (mStyle & STYLE_ITALICS) != 0; 326134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 327134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 328134545d2d8429615552931ef50a15ba2994d03f8Insun Kang boolean isUnderline() { 329134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return (mStyle & STYLE_UNDERLINE) != 0; 330134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 331134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 332134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int getColor() { 333134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return mColor; 334134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 335134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 336134545d2d8429615552931ef50a15ba2994d03f8Insun Kang @Override 337134545d2d8429615552931ef50a15ba2994d03f8Insun Kang public String toString() { 338134545d2d8429615552931ef50a15ba2994d03f8Insun Kang StringBuilder str = new StringBuilder(); 339134545d2d8429615552931ef50a15ba2994d03f8Insun Kang str.append("{"); 340134545d2d8429615552931ef50a15ba2994d03f8Insun Kang str.append(sColorMap[mColor]); 341134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if ((mStyle & STYLE_ITALICS) != 0) { 342134545d2d8429615552931ef50a15ba2994d03f8Insun Kang str.append(", ITALICS"); 343134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 344134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if ((mStyle & STYLE_UNDERLINE) != 0) { 345134545d2d8429615552931ef50a15ba2994d03f8Insun Kang str.append(", UNDERLINE"); 346134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 347134545d2d8429615552931ef50a15ba2994d03f8Insun Kang str.append("}"); 348134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 349134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return str.toString(); 350134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 351134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 352134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 353134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static class PAC extends StyleCode { 354134545d2d8429615552931ef50a15ba2994d03f8Insun Kang final int mRow; 355134545d2d8429615552931ef50a15ba2994d03f8Insun Kang final int mCol; 356134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 357134545d2d8429615552931ef50a15ba2994d03f8Insun Kang static PAC fromBytes(byte data1, byte data2) { 358134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int[] rowTable = {11, 1, 3, 12, 14, 5, 7, 9}; 359134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int row = rowTable[data1 & 0x07] + ((data2 & 0x20) >> 5); 360134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int style = 0; 361134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if ((data2 & 1) != 0) { 362134545d2d8429615552931ef50a15ba2994d03f8Insun Kang style |= STYLE_UNDERLINE; 363134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 364134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if ((data2 & 0x10) != 0) { 365134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // indent code 366134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int indent = (data2 >> 1) & 0x7; 367134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return new PAC(row, indent * 4, style, COLOR_WHITE); 368134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } else { 369134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // style code 370134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int color = (data2 >> 1) & 0x7; 371134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 372134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (color == COLOR_INVALID) { 373134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // WHITE ITALICS 374134545d2d8429615552931ef50a15ba2994d03f8Insun Kang color = COLOR_WHITE; 375134545d2d8429615552931ef50a15ba2994d03f8Insun Kang style |= STYLE_ITALICS; 376134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 377134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return new PAC(row, -1, style, color); 378134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 379134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 380134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 381134545d2d8429615552931ef50a15ba2994d03f8Insun Kang PAC(int row, int col, int style, int color) { 382134545d2d8429615552931ef50a15ba2994d03f8Insun Kang super(style, color); 383134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mRow = row; 384134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mCol = col; 385134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 386134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 387134545d2d8429615552931ef50a15ba2994d03f8Insun Kang boolean isIndentPAC() { 388134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return (mCol >= 0); 389134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 390134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 391134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int getRow() { 392134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return mRow; 393134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 394134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 395134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int getCol() { 396134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return mCol; 397134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 398134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 399134545d2d8429615552931ef50a15ba2994d03f8Insun Kang @Override 400134545d2d8429615552931ef50a15ba2994d03f8Insun Kang public String toString() { 401134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return String.format("{%d, %d}, %s", 402134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mRow, mCol, super.toString()); 403134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 404134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 405134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 406134545d2d8429615552931ef50a15ba2994d03f8Insun Kang /** 407134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * Mutable version of BackgroundSpan to facilitate text rendering with edge styles. 408134545d2d8429615552931ef50a15ba2994d03f8Insun Kang */ 409134545d2d8429615552931ef50a15ba2994d03f8Insun Kang public static class MutableBackgroundColorSpan extends CharacterStyle 410134545d2d8429615552931ef50a15ba2994d03f8Insun Kang implements UpdateAppearance { 411134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private int mColor; 412134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 413134545d2d8429615552931ef50a15ba2994d03f8Insun Kang MutableBackgroundColorSpan(int color) { 414134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mColor = color; 415134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 416134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 417134545d2d8429615552931ef50a15ba2994d03f8Insun Kang public void setBackgroundColor(int color) { 418134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mColor = color; 419134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 420134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 421134545d2d8429615552931ef50a15ba2994d03f8Insun Kang public int getBackgroundColor() { 422134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return mColor; 423134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 424134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 425134545d2d8429615552931ef50a15ba2994d03f8Insun Kang @Override 426134545d2d8429615552931ef50a15ba2994d03f8Insun Kang public void updateDrawState(TextPaint ds) { 427134545d2d8429615552931ef50a15ba2994d03f8Insun Kang ds.bgColor = mColor; 428134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 429134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 430134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 431134545d2d8429615552931ef50a15ba2994d03f8Insun Kang /* CCLineBuilder keeps track of displayable chars, as well as 432134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * MidRow styles and PACs, for a single line of CC memory. 433134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * 434134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * It generates styled text via getStyledText() method. 435134545d2d8429615552931ef50a15ba2994d03f8Insun Kang */ 436134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static class CCLineBuilder { 437134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private final StringBuilder mDisplayChars; 438134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private final StyleCode[] mMidRowStyles; 439134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private final StyleCode[] mPACStyles; 440134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 441134545d2d8429615552931ef50a15ba2994d03f8Insun Kang CCLineBuilder(String str) { 442134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mDisplayChars = new StringBuilder(str); 443134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mMidRowStyles = new StyleCode[mDisplayChars.length()]; 444134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mPACStyles = new StyleCode[mDisplayChars.length()]; 445134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 446134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 447134545d2d8429615552931ef50a15ba2994d03f8Insun Kang void setCharAt(int index, char ch) { 448134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mDisplayChars.setCharAt(index, ch); 449134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mMidRowStyles[index] = null; 450134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 451134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 452134545d2d8429615552931ef50a15ba2994d03f8Insun Kang void setMidRowAt(int index, StyleCode m) { 453134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mDisplayChars.setCharAt(index, ' '); 454134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mMidRowStyles[index] = m; 455134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 456134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 457134545d2d8429615552931ef50a15ba2994d03f8Insun Kang void setPACAt(int index, PAC pac) { 458134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mPACStyles[index] = pac; 459134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 460134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 461134545d2d8429615552931ef50a15ba2994d03f8Insun Kang char charAt(int index) { 462134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return mDisplayChars.charAt(index); 463134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 464134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 465134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int length() { 466134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return mDisplayChars.length(); 467134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 468134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 469134545d2d8429615552931ef50a15ba2994d03f8Insun Kang void applyStyleSpan( 470134545d2d8429615552931ef50a15ba2994d03f8Insun Kang SpannableStringBuilder styledText, 471134545d2d8429615552931ef50a15ba2994d03f8Insun Kang StyleCode s, int start, int end) { 472134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (s.isItalics()) { 473134545d2d8429615552931ef50a15ba2994d03f8Insun Kang styledText.setSpan( 474134545d2d8429615552931ef50a15ba2994d03f8Insun Kang new StyleSpan(android.graphics.Typeface.ITALIC), 475134545d2d8429615552931ef50a15ba2994d03f8Insun Kang start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 476134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 477134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (s.isUnderline()) { 478134545d2d8429615552931ef50a15ba2994d03f8Insun Kang styledText.setSpan( 479134545d2d8429615552931ef50a15ba2994d03f8Insun Kang new UnderlineSpan(), 480134545d2d8429615552931ef50a15ba2994d03f8Insun Kang start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 481134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 482134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 483134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 484134545d2d8429615552931ef50a15ba2994d03f8Insun Kang SpannableStringBuilder getStyledText(CaptionStyle captionStyle) { 485134545d2d8429615552931ef50a15ba2994d03f8Insun Kang SpannableStringBuilder styledText = new SpannableStringBuilder(mDisplayChars); 486134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int start = -1, next = 0; 487134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int styleStart = -1; 488134545d2d8429615552931ef50a15ba2994d03f8Insun Kang StyleCode curStyle = null; 489134545d2d8429615552931ef50a15ba2994d03f8Insun Kang while (next < mDisplayChars.length()) { 490134545d2d8429615552931ef50a15ba2994d03f8Insun Kang StyleCode newStyle = null; 491134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (mMidRowStyles[next] != null) { 492134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // apply mid-row style change 493134545d2d8429615552931ef50a15ba2994d03f8Insun Kang newStyle = mMidRowStyles[next]; 494134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } else if (mPACStyles[next] != null && (styleStart < 0 || start < 0)) { 495134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // apply PAC style change, only if: 496134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // 1. no style set, or 497134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // 2. style set, but prev char is none-displayable 498134545d2d8429615552931ef50a15ba2994d03f8Insun Kang newStyle = mPACStyles[next]; 499134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 500134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (newStyle != null) { 501134545d2d8429615552931ef50a15ba2994d03f8Insun Kang curStyle = newStyle; 502134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (styleStart >= 0 && start >= 0) { 503134545d2d8429615552931ef50a15ba2994d03f8Insun Kang applyStyleSpan(styledText, newStyle, styleStart, next); 504134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 505134545d2d8429615552931ef50a15ba2994d03f8Insun Kang styleStart = next; 506134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 507134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 508134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (mDisplayChars.charAt(next) != TS) { 509134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (start < 0) { 510134545d2d8429615552931ef50a15ba2994d03f8Insun Kang start = next; 511134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 512134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } else if (start >= 0) { 513134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int expandedStart = mDisplayChars.charAt(start) == ' ' ? start : start - 1; 514134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int expandedEnd = mDisplayChars.charAt(next - 1) == ' ' ? next : next + 1; 515134545d2d8429615552931ef50a15ba2994d03f8Insun Kang styledText.setSpan( 516134545d2d8429615552931ef50a15ba2994d03f8Insun Kang new MutableBackgroundColorSpan(captionStyle.backgroundColor), 517134545d2d8429615552931ef50a15ba2994d03f8Insun Kang expandedStart, expandedEnd, 518134545d2d8429615552931ef50a15ba2994d03f8Insun Kang Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 519134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (styleStart >= 0) { 520134545d2d8429615552931ef50a15ba2994d03f8Insun Kang applyStyleSpan(styledText, curStyle, styleStart, expandedEnd); 521134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 522134545d2d8429615552931ef50a15ba2994d03f8Insun Kang start = -1; 523134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 524134545d2d8429615552931ef50a15ba2994d03f8Insun Kang next++; 525134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 526134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 527134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return styledText; 528134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 529134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 530134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 531134545d2d8429615552931ef50a15ba2994d03f8Insun Kang /* 532134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * CCMemory models a console-style display. 533134545d2d8429615552931ef50a15ba2994d03f8Insun Kang */ 534134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static class CCMemory { 535134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private final String mBlankLine; 536134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private final CCLineBuilder[] mLines = new CCLineBuilder[MAX_ROWS + 2]; 537134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private int mRow; 538134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private int mCol; 539134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 540134545d2d8429615552931ef50a15ba2994d03f8Insun Kang CCMemory() { 541134545d2d8429615552931ef50a15ba2994d03f8Insun Kang char[] blank = new char[MAX_COLS + 2]; 542134545d2d8429615552931ef50a15ba2994d03f8Insun Kang Arrays.fill(blank, TS); 543134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mBlankLine = new String(blank); 544134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 545134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 546134545d2d8429615552931ef50a15ba2994d03f8Insun Kang void erase() { 547134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // erase all lines 548134545d2d8429615552931ef50a15ba2994d03f8Insun Kang for (int i = 0; i < mLines.length; i++) { 549134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mLines[i] = null; 550134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 551134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mRow = MAX_ROWS; 552134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mCol = 1; 553134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 554134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 555134545d2d8429615552931ef50a15ba2994d03f8Insun Kang void der() { 556134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (mLines[mRow] != null) { 557134545d2d8429615552931ef50a15ba2994d03f8Insun Kang for (int i = 0; i < mCol; i++) { 558134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (mLines[mRow].charAt(i) != TS) { 559134545d2d8429615552931ef50a15ba2994d03f8Insun Kang for (int j = mCol; j < mLines[mRow].length(); j++) { 560134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mLines[j].setCharAt(j, TS); 561134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 562134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return; 563134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 564134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 565134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mLines[mRow] = null; 566134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 567134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 568134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 569134545d2d8429615552931ef50a15ba2994d03f8Insun Kang void tab(int tabs) { 570134545d2d8429615552931ef50a15ba2994d03f8Insun Kang moveCursorByCol(tabs); 571134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 572134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 573134545d2d8429615552931ef50a15ba2994d03f8Insun Kang void bs() { 574134545d2d8429615552931ef50a15ba2994d03f8Insun Kang moveCursorByCol(-1); 575134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (mLines[mRow] != null) { 576134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mLines[mRow].setCharAt(mCol, TS); 577134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (mCol == MAX_COLS - 1) { 578134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // Spec recommendation: 579134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // if cursor was at col 32, move cursor 580134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // back to col 31 and erase both col 31&32 581134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mLines[mRow].setCharAt(MAX_COLS, TS); 582134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 583134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 584134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 585134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 586134545d2d8429615552931ef50a15ba2994d03f8Insun Kang void cr() { 587134545d2d8429615552931ef50a15ba2994d03f8Insun Kang moveCursorTo(mRow + 1, 1); 588134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 589134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 590134545d2d8429615552931ef50a15ba2994d03f8Insun Kang void rollUp(int windowSize) { 591134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int i; 592134545d2d8429615552931ef50a15ba2994d03f8Insun Kang for (i = 0; i <= mRow - windowSize; i++) { 593134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mLines[i] = null; 594134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 595134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int startRow = mRow - windowSize + 1; 596134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (startRow < 1) { 597134545d2d8429615552931ef50a15ba2994d03f8Insun Kang startRow = 1; 598134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 599134545d2d8429615552931ef50a15ba2994d03f8Insun Kang for (i = startRow; i < mRow; i++) { 600134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mLines[i] = mLines[i + 1]; 601134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 602134545d2d8429615552931ef50a15ba2994d03f8Insun Kang for (i = mRow; i < mLines.length; i++) { 603134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // clear base row 604134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mLines[i] = null; 605134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 606134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // default to col 1, in case PAC is not sent 607134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mCol = 1; 608134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 609134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 610134545d2d8429615552931ef50a15ba2994d03f8Insun Kang void writeText(String text) { 611134545d2d8429615552931ef50a15ba2994d03f8Insun Kang for (int i = 0; i < text.length(); i++) { 612134545d2d8429615552931ef50a15ba2994d03f8Insun Kang getLineBuffer(mRow).setCharAt(mCol, text.charAt(i)); 613134545d2d8429615552931ef50a15ba2994d03f8Insun Kang moveCursorByCol(1); 614134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 615134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 616134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 617134545d2d8429615552931ef50a15ba2994d03f8Insun Kang void writeMidRowCode(StyleCode m) { 618134545d2d8429615552931ef50a15ba2994d03f8Insun Kang getLineBuffer(mRow).setMidRowAt(mCol, m); 619134545d2d8429615552931ef50a15ba2994d03f8Insun Kang moveCursorByCol(1); 620134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 621134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 622134545d2d8429615552931ef50a15ba2994d03f8Insun Kang void writePAC(PAC pac) { 623134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (pac.isIndentPAC()) { 624134545d2d8429615552931ef50a15ba2994d03f8Insun Kang moveCursorTo(pac.getRow(), pac.getCol()); 625134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } else { 626134545d2d8429615552931ef50a15ba2994d03f8Insun Kang moveCursorTo(pac.getRow(), 1); 627134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 628134545d2d8429615552931ef50a15ba2994d03f8Insun Kang getLineBuffer(mRow).setPACAt(mCol, pac); 629134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 630134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 631134545d2d8429615552931ef50a15ba2994d03f8Insun Kang SpannableStringBuilder[] getStyledText(CaptionStyle captionStyle) { 632134545d2d8429615552931ef50a15ba2994d03f8Insun Kang ArrayList<SpannableStringBuilder> rows = new ArrayList<>(MAX_ROWS); 633134545d2d8429615552931ef50a15ba2994d03f8Insun Kang for (int i = 1; i <= MAX_ROWS; i++) { 634134545d2d8429615552931ef50a15ba2994d03f8Insun Kang rows.add(mLines[i] != null ? mLines[i].getStyledText(captionStyle) : null); 635134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 636134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return rows.toArray(new SpannableStringBuilder[MAX_ROWS]); 637134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 638134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 639134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static int clamp(int x, int min, int max) { 640134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return x < min ? min : (x > max ? max : x); 641134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 642134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 643134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private void moveCursorTo(int row, int col) { 644134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mRow = clamp(row, 1, MAX_ROWS); 645134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mCol = clamp(col, 1, MAX_COLS); 646134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 647134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 648134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private void moveCursorToRow(int row) { 649134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mRow = clamp(row, 1, MAX_ROWS); 650134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 651134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 652134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private void moveCursorByCol(int col) { 653134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mCol = clamp(mCol + col, 1, MAX_COLS); 654134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 655134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 656134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private void moveBaselineTo(int baseRow, int windowSize) { 657134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (mRow == baseRow) { 658134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return; 659134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 660134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int actualWindowSize = windowSize; 661134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (baseRow < actualWindowSize) { 662134545d2d8429615552931ef50a15ba2994d03f8Insun Kang actualWindowSize = baseRow; 663134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 664134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (mRow < actualWindowSize) { 665134545d2d8429615552931ef50a15ba2994d03f8Insun Kang actualWindowSize = mRow; 666134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 667134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 668134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int i; 669134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (baseRow < mRow) { 670134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // copy from bottom to top row 671134545d2d8429615552931ef50a15ba2994d03f8Insun Kang for (i = actualWindowSize - 1; i >= 0; i--) { 672134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mLines[baseRow - i] = mLines[mRow - i]; 673134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 674134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } else { 675134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // copy from top to bottom row 676134545d2d8429615552931ef50a15ba2994d03f8Insun Kang for (i = 0; i < actualWindowSize; i++) { 677134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mLines[baseRow - i] = mLines[mRow - i]; 678134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 679134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 680134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // clear rest of the rows 681134545d2d8429615552931ef50a15ba2994d03f8Insun Kang for (i = 0; i <= baseRow - windowSize; i++) { 682134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mLines[i] = null; 683134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 684134545d2d8429615552931ef50a15ba2994d03f8Insun Kang for (i = baseRow + 1; i < mLines.length; i++) { 685134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mLines[i] = null; 686134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 687134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 688134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 689134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private CCLineBuilder getLineBuffer(int row) { 690134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (mLines[row] == null) { 691134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mLines[row] = new CCLineBuilder(mBlankLine); 692134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 693134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return mLines[row]; 694134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 695134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 696134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 697134545d2d8429615552931ef50a15ba2994d03f8Insun Kang /* 698134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * CCData parses the raw CC byte pair into displayable chars, 699134545d2d8429615552931ef50a15ba2994d03f8Insun Kang * misc control codes, Mid-Row or Preamble Address Codes. 700134545d2d8429615552931ef50a15ba2994d03f8Insun Kang */ 701134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static class CCData { 702134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private final byte mType; 703134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private final byte mData1; 704134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private final byte mData2; 705134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 706134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final String[] sCtrlCodeMap = { 707134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "RCL", "BS" , "AOF", "AON", 708134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "DER", "RU2", "RU3", "RU4", 709134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "FON", "RDC", "TR" , "RTD", 710134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "EDM", "CR" , "ENM", "EOC", 711134545d2d8429615552931ef50a15ba2994d03f8Insun Kang }; 712134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 713134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final String[] sSpecialCharMap = { 714134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00AE", 715134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00B0", 716134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00BD", 717134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00BF", 718134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u2122", 719134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00A2", 720134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00A3", 721134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u266A", // Eighth note 722134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00E0", 723134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00A0", // Transparent space 724134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00E8", 725134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00E2", 726134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00EA", 727134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00EE", 728134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00F4", 729134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00FB", 730134545d2d8429615552931ef50a15ba2994d03f8Insun Kang }; 731134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 732134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final String[] sSpanishCharMap = { 733134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // Spanish and misc chars 734134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00C1", // A 735134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00C9", // E 736134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00D3", // I 737134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00DA", // O 738134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00DC", // U 739134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00FC", // u 740134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u2018", // opening single quote 741134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00A1", // inverted exclamation mark 742134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "*", 743134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "'", 744134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u2014", // em dash 745134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00A9", // Copyright 746134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u2120", // Servicemark 747134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u2022", // round bullet 748134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u201C", // opening double quote 749134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u201D", // closing double quote 750134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // French 751134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00C0", 752134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00C2", 753134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00C7", 754134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00C8", 755134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00CA", 756134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00CB", 757134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00EB", 758134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00CE", 759134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00CF", 760134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00EF", 761134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00D4", 762134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00D9", 763134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00F9", 764134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00DB", 765134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00AB", 766134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00BB" 767134545d2d8429615552931ef50a15ba2994d03f8Insun Kang }; 768134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 769134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private static final String[] sProtugueseCharMap = { 770134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // Portuguese 771134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00C3", 772134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00E3", 773134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00CD", 774134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00CC", 775134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00EC", 776134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00D2", 777134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00F2", 778134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00D5", 779134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00F5", 780134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "{", 781134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "}", 782134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\\", 783134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "^", 784134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "_", 785134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "|", 786134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "~", 787134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // German and misc chars 788134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00C4", 789134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00E4", 790134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00D6", 791134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00F6", 792134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00DF", 793134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00A5", 794134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00A4", 795134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u2502", // vertical bar 796134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00C5", 797134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00E5", 798134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00D8", 799134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u00F8", 800134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u250C", // top-left corner 801134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u2510", // top-right corner 802134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u2514", // lower-left corner 803134545d2d8429615552931ef50a15ba2994d03f8Insun Kang "\u2518", // lower-right corner 804134545d2d8429615552931ef50a15ba2994d03f8Insun Kang }; 805134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 806134545d2d8429615552931ef50a15ba2994d03f8Insun Kang static CCData[] fromByteArray(byte[] data) { 807134545d2d8429615552931ef50a15ba2994d03f8Insun Kang CCData[] ccData = new CCData[data.length / 3]; 808134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 809134545d2d8429615552931ef50a15ba2994d03f8Insun Kang for (int i = 0; i < ccData.length; i++) { 810134545d2d8429615552931ef50a15ba2994d03f8Insun Kang ccData[i] = new CCData( 811134545d2d8429615552931ef50a15ba2994d03f8Insun Kang data[i * 3], 812134545d2d8429615552931ef50a15ba2994d03f8Insun Kang data[i * 3 + 1], 813134545d2d8429615552931ef50a15ba2994d03f8Insun Kang data[i * 3 + 2]); 814134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 815134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 816134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return ccData; 817134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 818134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 819134545d2d8429615552931ef50a15ba2994d03f8Insun Kang CCData(byte type, byte data1, byte data2) { 820134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mType = type; 821134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mData1 = data1; 822134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mData2 = data2; 823134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 824134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 825134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int getCtrlCode() { 826134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if ((mData1 == 0x14 || mData1 == 0x1c) 827134545d2d8429615552931ef50a15ba2994d03f8Insun Kang && mData2 >= 0x20 && mData2 <= 0x2f) { 828134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return mData2; 829134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 830134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return INVALID; 831134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 832134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 833134545d2d8429615552931ef50a15ba2994d03f8Insun Kang StyleCode getMidRow() { 834134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // only support standard Mid-row codes, ignore 835134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // optional background/foreground mid-row codes 836134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if ((mData1 == 0x11 || mData1 == 0x19) 837134545d2d8429615552931ef50a15ba2994d03f8Insun Kang && mData2 >= 0x20 && mData2 <= 0x2f) { 838134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return StyleCode.fromByte(mData2); 839134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 840134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return null; 841134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 842134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 843134545d2d8429615552931ef50a15ba2994d03f8Insun Kang PAC getPAC() { 844134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if ((mData1 & 0x70) == 0x10 845134545d2d8429615552931ef50a15ba2994d03f8Insun Kang && (mData2 & 0x40) == 0x40 846134545d2d8429615552931ef50a15ba2994d03f8Insun Kang && ((mData1 & 0x07) != 0 || (mData2 & 0x20) == 0)) { 847134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return PAC.fromBytes(mData1, mData2); 848134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 849134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return null; 850134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 851134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 852134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int getTabOffset() { 853134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if ((mData1 == 0x17 || mData1 == 0x1f) 854134545d2d8429615552931ef50a15ba2994d03f8Insun Kang && mData2 >= 0x21 && mData2 <= 0x23) { 855134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return mData2 & 0x3; 856134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 857134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return 0; 858134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 859134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 860134545d2d8429615552931ef50a15ba2994d03f8Insun Kang boolean isDisplayableChar() { 861134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return isBasicChar() || isSpecialChar() || isExtendedChar(); 862134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 863134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 864134545d2d8429615552931ef50a15ba2994d03f8Insun Kang String getDisplayText() { 865134545d2d8429615552931ef50a15ba2994d03f8Insun Kang String str = getBasicChars(); 866134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 867134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (str == null) { 868134545d2d8429615552931ef50a15ba2994d03f8Insun Kang str = getSpecialChar(); 869134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 870134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (str == null) { 871134545d2d8429615552931ef50a15ba2994d03f8Insun Kang str = getExtendedChar(); 872134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 873134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 874134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 875134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return str; 876134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 877134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 878134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private String ctrlCodeToString(int ctrlCode) { 879134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return sCtrlCodeMap[ctrlCode - 0x20]; 880134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 881134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 882134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private boolean isBasicChar() { 883134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return mData1 >= 0x20 && mData1 <= 0x7f; 884134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 885134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 886134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private boolean isSpecialChar() { 887134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return ((mData1 == 0x11 || mData1 == 0x19) 888134545d2d8429615552931ef50a15ba2994d03f8Insun Kang && mData2 >= 0x30 && mData2 <= 0x3f); 889134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 890134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 891134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private boolean isExtendedChar() { 892134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return ((mData1 == 0x12 || mData1 == 0x1A 893134545d2d8429615552931ef50a15ba2994d03f8Insun Kang || mData1 == 0x13 || mData1 == 0x1B) 894134545d2d8429615552931ef50a15ba2994d03f8Insun Kang && mData2 >= 0x20 && mData2 <= 0x3f); 895134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 896134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 897134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private char getBasicChar(byte data) { 898134545d2d8429615552931ef50a15ba2994d03f8Insun Kang char c; 899134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // replace the non-ASCII ones 900134545d2d8429615552931ef50a15ba2994d03f8Insun Kang switch (data) { 901134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case 0x2A: c = '\u00E1'; break; 902134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case 0x5C: c = '\u00E9'; break; 903134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case 0x5E: c = '\u00ED'; break; 904134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case 0x5F: c = '\u00F3'; break; 905134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case 0x60: c = '\u00FA'; break; 906134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case 0x7B: c = '\u00E7'; break; 907134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case 0x7C: c = '\u00F7'; break; 908134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case 0x7D: c = '\u00D1'; break; 909134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case 0x7E: c = '\u00F1'; break; 910134545d2d8429615552931ef50a15ba2994d03f8Insun Kang case 0x7F: c = '\u2588'; break; // Full block 911134545d2d8429615552931ef50a15ba2994d03f8Insun Kang default: c = (char) data; break; 912134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 913134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return c; 914134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 915134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 916134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private String getBasicChars() { 917134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (mData1 >= 0x20 && mData1 <= 0x7f) { 918134545d2d8429615552931ef50a15ba2994d03f8Insun Kang StringBuilder builder = new StringBuilder(2); 919134545d2d8429615552931ef50a15ba2994d03f8Insun Kang builder.append(getBasicChar(mData1)); 920134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (mData2 >= 0x20 && mData2 <= 0x7f) { 921134545d2d8429615552931ef50a15ba2994d03f8Insun Kang builder.append(getBasicChar(mData2)); 922134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 923134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return builder.toString(); 924134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 925134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 926134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return null; 927134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 928134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 929134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private String getSpecialChar() { 930134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if ((mData1 == 0x11 || mData1 == 0x19) 931134545d2d8429615552931ef50a15ba2994d03f8Insun Kang && mData2 >= 0x30 && mData2 <= 0x3f) { 932134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return sSpecialCharMap[mData2 - 0x30]; 933134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 934134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 935134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return null; 936134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 937134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 938134545d2d8429615552931ef50a15ba2994d03f8Insun Kang private String getExtendedChar() { 939134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if ((mData1 == 0x12 || mData1 == 0x1A) && mData2 >= 0x20 && mData2 <= 0x3f) { 940134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // 1 Spanish/French char 941134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return sSpanishCharMap[mData2 - 0x20]; 942134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } else if ((mData1 == 0x13 || mData1 == 0x1B) && mData2 >= 0x20 && mData2 <= 0x3f) { 943134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // 1 Portuguese/German/Danish char 944134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return sProtugueseCharMap[mData2 - 0x20]; 945134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 946134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 947134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return null; 948134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 949134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 950134545d2d8429615552931ef50a15ba2994d03f8Insun Kang @Override 951134545d2d8429615552931ef50a15ba2994d03f8Insun Kang public String toString() { 952134545d2d8429615552931ef50a15ba2994d03f8Insun Kang String str; 953134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 954134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (mData1 < 0x10 && mData2 < 0x10) { 955134545d2d8429615552931ef50a15ba2994d03f8Insun Kang // Null Pad, ignore 956134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return String.format("[%d]Null: %02x %02x", mType, mData1, mData2); 957134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 958134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 959134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int ctrlCode = getCtrlCode(); 960134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (ctrlCode != INVALID) { 961134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return String.format("[%d]%s", mType, ctrlCodeToString(ctrlCode)); 962134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 963134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 964134545d2d8429615552931ef50a15ba2994d03f8Insun Kang int tabOffset = getTabOffset(); 965134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (tabOffset > 0) { 966134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return String.format("[%d]Tab%d", mType, tabOffset); 967134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 968134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 969134545d2d8429615552931ef50a15ba2994d03f8Insun Kang PAC pac = getPAC(); 970134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (pac != null) { 971134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return String.format("[%d]PAC: %s", mType, pac.toString()); 972134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 973134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 974134545d2d8429615552931ef50a15ba2994d03f8Insun Kang StyleCode m = getMidRow(); 975134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (m != null) { 976134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return String.format("[%d]Mid-row: %s", mType, m.toString()); 977134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 978134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 979134545d2d8429615552931ef50a15ba2994d03f8Insun Kang if (isDisplayableChar()) { 980134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return String.format("[%d]Displayable: %s (%02x %02x)", 981134545d2d8429615552931ef50a15ba2994d03f8Insun Kang mType, getDisplayText(), mData1, mData2); 982134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 983134545d2d8429615552931ef50a15ba2994d03f8Insun Kang 984134545d2d8429615552931ef50a15ba2994d03f8Insun Kang return String.format("[%d]Invalid: %02x %02x", mType, mData1, mData2); 985134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 986134545d2d8429615552931ef50a15ba2994d03f8Insun Kang } 987134545d2d8429615552931ef50a15ba2994d03f8Insun Kang} 988