1410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey/*
2410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * Copyright (C) 2013 The Android Open Source Project
3410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey *
4410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
5410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * you may not use this file except in compliance with the License.
6410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * You may obtain a copy of the License at
7410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey *
8410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
9410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey *
10410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * Unless required by applicable law or agreed to in writing, software
11410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
12410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * See the License for the specific language governing permissions and
14410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * limitations under the License.
15410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey */
16410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
17410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeypackage com.android.terminal;
18410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
1900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkeyimport static com.android.terminal.Terminal.TAG;
2000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey
21410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeyimport android.content.Context;
22410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeyimport android.graphics.Paint;
23410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeyimport android.graphics.Paint.FontMetrics;
24479bd643981271fb0edf756ae5915e44a7352c4dJeff Sharkeyimport android.graphics.Typeface;
2500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkeyimport android.os.Parcelable;
2600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkeyimport android.util.AttributeSet;
27410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeyimport android.util.Log;
2800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkeyimport android.view.KeyEvent;
2900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkeyimport android.view.View;
3000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkeyimport android.view.ViewGroup;
3126c9bb3a573b236f009654903461e496410a4147Michael Wrightimport android.view.inputmethod.BaseInputConnection;
3226c9bb3a573b236f009654903461e496410a4147Michael Wrightimport android.view.inputmethod.EditorInfo;
3326c9bb3a573b236f009654903461e496410a4147Michael Wrightimport android.view.inputmethod.InputConnection;
341c4704e650a86914e3afdc57ce9df4f95d7c7d44Tom Marshallimport android.view.inputmethod.InputMethodManager;
351c4704e650a86914e3afdc57ce9df4f95d7c7d44Tom Marshallimport android.widget.AdapterView;
3600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkeyimport android.widget.BaseAdapter;
3700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkeyimport android.widget.ListView;
38410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
39a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkeyimport com.android.terminal.Terminal.CellRun;
40410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeyimport com.android.terminal.Terminal.TerminalClient;
41410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
42410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey/**
43410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * Rendered contents of a {@link Terminal} session.
44410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey */
4500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkeypublic class TerminalView extends ListView {
46de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey    private static final boolean LOGD = true;
47410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
4800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    private static final boolean SCROLL_ON_DAMAGE = false;
4900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    private static final boolean SCROLL_ON_INPUT = true;
5000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey
5100b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    private Terminal mTerm;
52a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey
5300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    private boolean mScrolled;
54410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
5500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    private int mRows;
5600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    private int mCols;
5700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    private int mScrollRows;
58410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
5900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    private final TerminalMetrics mMetrics = new TerminalMetrics();
6000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    private final TerminalKeys mTermKeys = new TerminalKeys();
61a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey
6200b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    /**
6300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey     * Metrics shared between all {@link TerminalLineView} children. Locking
6400b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey     * provided by main thread.
6500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey     */
6600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    static class TerminalMetrics {
6700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        private static final int MAX_RUN_LENGTH = 128;
68de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey
6900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        final Paint bgPaint = new Paint();
7000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        final Paint textPaint = new Paint();
715b68e8ad082625206492ca9baf889f8c5427eb01Tom Marshall        final Paint cursorPaint = new Paint();
7226c9bb3a573b236f009654903461e496410a4147Michael Wright
7300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        /** Run of cells used when drawing */
7400b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        final CellRun run;
7500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        /** Screen coordinates to draw chars into */
7600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        final float[] pos;
77410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
7800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        int charTop;
7900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        int charWidth;
8000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        int charHeight;
81a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey
8200b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        public TerminalMetrics() {
8300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            run = new Terminal.CellRun();
8400b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            run.data = new char[MAX_RUN_LENGTH];
8500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey
8600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            // Positions of each possible cell
8700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            // TODO: make sure this works with surrogate pairs
8800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            pos = new float[MAX_RUN_LENGTH * 2];
8900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            setTextSize(20);
90410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey        }
91410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
9200b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        public void setTextSize(float textSize) {
9300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            textPaint.setTypeface(Typeface.MONOSPACE);
9400b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            textPaint.setAntiAlias(true);
9500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            textPaint.setTextSize(textSize);
9600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey
9700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            // Read metrics to get exact pixel dimensions
9800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            final FontMetrics fm = textPaint.getFontMetrics();
9900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            charTop = (int) Math.ceil(fm.top);
10000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey
10100b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            final float[] widths = new float[1];
10200b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            textPaint.getTextWidths("X", widths);
10300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            charWidth = (int) Math.ceil(widths[0]);
10400b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            charHeight = (int) Math.ceil(fm.descent - fm.top);
10500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey
10600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            // Update drawing positions
10700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            for (int i = 0; i < MAX_RUN_LENGTH; i++) {
10800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey                pos[i * 2] = i * charWidth;
10900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey                pos[(i * 2) + 1] = -charTop;
11000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            }
1116a142b6d4831c3841b6be1705fc97c9b75a7c9d1Jeff Sharkey        }
11200b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    }
1136a142b6d4831c3841b6be1705fc97c9b75a7c9d1Jeff Sharkey
1141c4704e650a86914e3afdc57ce9df4f95d7c7d44Tom Marshall    private final AdapterView.OnItemClickListener mClickListener = new AdapterView.OnItemClickListener() {
1151c4704e650a86914e3afdc57ce9df4f95d7c7d44Tom Marshall        @Override
1161c4704e650a86914e3afdc57ce9df4f95d7c7d44Tom Marshall        public void onItemClick(AdapterView<?> parent, View v, int pos, long id) {
1171c4704e650a86914e3afdc57ce9df4f95d7c7d44Tom Marshall            if (parent.requestFocus()) {
1181c4704e650a86914e3afdc57ce9df4f95d7c7d44Tom Marshall                InputMethodManager imm = (InputMethodManager) parent.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
1191c4704e650a86914e3afdc57ce9df4f95d7c7d44Tom Marshall                imm.showSoftInput(parent, InputMethodManager.SHOW_IMPLICIT);
1201c4704e650a86914e3afdc57ce9df4f95d7c7d44Tom Marshall            }
1211c4704e650a86914e3afdc57ce9df4f95d7c7d44Tom Marshall        }
1221c4704e650a86914e3afdc57ce9df4f95d7c7d44Tom Marshall    };
1231c4704e650a86914e3afdc57ce9df4f95d7c7d44Tom Marshall
12400b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    private final Runnable mDamageRunnable = new Runnable() {
1256a142b6d4831c3841b6be1705fc97c9b75a7c9d1Jeff Sharkey        @Override
12600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        public void run() {
12700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            invalidateViews();
12800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            if (SCROLL_ON_DAMAGE) {
12900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey                scrollToBottom(true);
13000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            }
131410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey        }
132410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey    };
133410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
134de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey    public TerminalView(Context context) {
13500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        this(context, null);
13600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    }
137a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey
13800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    public TerminalView(Context context, AttributeSet attrs) {
13900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        this(context, attrs, com.android.internal.R.attr.listViewStyle);
14000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    }
141a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey
14200b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    public TerminalView(Context context, AttributeSet attrs, int defStyle) {
14300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        super(context, attrs, defStyle);
144410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
14500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        setBackground(null);
14600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        setDivider(null);
14726c9bb3a573b236f009654903461e496410a4147Michael Wright
14826c9bb3a573b236f009654903461e496410a4147Michael Wright        setFocusable(true);
14926c9bb3a573b236f009654903461e496410a4147Michael Wright        setFocusableInTouchMode(true);
15026c9bb3a573b236f009654903461e496410a4147Michael Wright
15100b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        setAdapter(mAdapter);
15200b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        setOnKeyListener(mKeyListener);
1531c4704e650a86914e3afdc57ce9df4f95d7c7d44Tom Marshall
1541c4704e650a86914e3afdc57ce9df4f95d7c7d44Tom Marshall        setOnItemClickListener(mClickListener);
155410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey    }
156410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
15700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    private final BaseAdapter mAdapter = new BaseAdapter() {
15800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        @Override
15900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        public View getView(int position, View convertView, ViewGroup parent) {
16000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            final TerminalLineView view;
16100b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            if (convertView != null) {
16200b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey                view = (TerminalLineView) convertView;
16300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            } else {
16400b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey                view = new TerminalLineView(parent.getContext(), mTerm, mMetrics);
16500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            }
16600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey
16700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            view.pos = position;
16800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            view.row = posToRow(position);
16900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            view.cols = mCols;
17000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            return view;
171de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        }
172de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey
17300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        @Override
17400b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        public long getItemId(int position) {
17500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            return position;
176de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        }
177410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
17800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        @Override
17900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        public Object getItem(int position) {
18000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            return null;
181de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        }
182410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
18300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        @Override
18400b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        public int getCount() {
18500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            if (mTerm != null) {
18600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey                return mRows + mScrollRows;
18700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            } else {
18800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey                return 0;
18900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            }
190a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey        }
19100b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    };
192a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey
19300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    private TerminalClient mClient = new TerminalClient() {
19400b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        @Override
19500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        public void onDamage(final int startRow, final int endRow, int startCol, int endCol) {
19600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            post(mDamageRunnable);
19700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        }
198410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
19900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        @Override
20000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        public void onMoveRect(int destStartRow, int destEndRow, int destStartCol, int destEndCol,
20100b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey                int srcStartRow, int srcEndRow, int srcStartCol, int srcEndCol) {
20200b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            post(mDamageRunnable);
203410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey        }
204410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
20500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        @Override
2065b68e8ad082625206492ca9baf889f8c5427eb01Tom Marshall        public void onMoveCursor(int posRow, int posCol, int oldPosRow, int oldPosCol, int visible) {
2075b68e8ad082625206492ca9baf889f8c5427eb01Tom Marshall            post(mDamageRunnable);
2085b68e8ad082625206492ca9baf889f8c5427eb01Tom Marshall        }
2095b68e8ad082625206492ca9baf889f8c5427eb01Tom Marshall
2105b68e8ad082625206492ca9baf889f8c5427eb01Tom Marshall        @Override
21100b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        public void onBell() {
21200b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            Log.i(TAG, "DING!");
213410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey        }
21400b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    };
21500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey
21600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    private int rowToPos(int row) {
21700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        return row + mScrollRows;
218410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey    }
219410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
22000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    private int posToRow(int pos) {
22100b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        return pos - mScrollRows;
22200b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    }
223410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
22400b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    private View.OnKeyListener mKeyListener = new OnKeyListener() {
22500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        @Override
22600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        public boolean onKey(View v, int keyCode, KeyEvent event) {
22700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            final boolean res = mTermKeys.onKey(v, keyCode, event);
22800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            if (res && SCROLL_ON_INPUT) {
22900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey                scrollToBottom(true);
23000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            }
23100b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            return res;
232de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        }
23300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    };
234de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey
23500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    @Override
23600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    public void onRestoreInstanceState(Parcelable state) {
23700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        super.onRestoreInstanceState(state);
23800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        mScrolled = true;
23900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    }
240410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
24100b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    @Override
24200b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    protected void onAttachedToWindow() {
24300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        super.onAttachedToWindow();
24400b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        if (!mScrolled) {
24500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            scrollToBottom(false);
24600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        }
24700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    }
248410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
24900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    @Override
25000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
25100b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        super.onSizeChanged(w, h, oldw, oldh);
2529cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey
25300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        final int rows = h / mMetrics.charHeight;
25400b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        final int cols = w / mMetrics.charWidth;
25500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        final int scrollRows = mScrollRows;
2569cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey
25700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        final boolean sizeChanged = (rows != mRows || cols != mCols || scrollRows != mScrollRows);
25800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        if (mTerm != null && sizeChanged) {
25900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            mTerm.resize(rows, cols, scrollRows);
260410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
26100b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            mRows = rows;
26200b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            mCols = cols;
26300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            mScrollRows = scrollRows;
264410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
26500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            mAdapter.notifyDataSetChanged();
26600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        }
26700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    }
268410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
26900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    public void scrollToBottom(boolean animate) {
27000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        final int dur = animate ? 250 : 0;
27100b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        smoothScrollToPositionFromTop(getCount(), 0, dur);
27200b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        mScrolled = true;
27300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    }
2749cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey
27500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    public void setTerminal(Terminal term) {
27600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        final Terminal orig = mTerm;
27700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        if (orig != null) {
27800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            orig.setClient(null);
27900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        }
28000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        mTerm = term;
28100b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        mScrolled = false;
28200b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        if (term != null) {
28300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            term.setClient(mClient);
28400b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            mTermKeys.setTerminal(term);
2859cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey
2865b68e8ad082625206492ca9baf889f8c5427eb01Tom Marshall            mMetrics.cursorPaint.setColor(0xfff0f0f0);
2875b68e8ad082625206492ca9baf889f8c5427eb01Tom Marshall
28800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            // Populate any current settings
28900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            mRows = mTerm.getRows();
29000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            mCols = mTerm.getCols();
29100b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            mScrollRows = mTerm.getScrollRows();
29200b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            mAdapter.notifyDataSetChanged();
29300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        }
29400b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    }
295410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
29600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    public Terminal getTerminal() {
29700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        return mTerm;
29800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    }
299410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
30000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    public void setTextSize(float textSize) {
30100b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        mMetrics.setTextSize(textSize);
302410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
30300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        // Layout will kick off terminal resize when needed
30400b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        requestLayout();
305410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey    }
30626c9bb3a573b236f009654903461e496410a4147Michael Wright
30726c9bb3a573b236f009654903461e496410a4147Michael Wright    @Override
30826c9bb3a573b236f009654903461e496410a4147Michael Wright    public boolean onCheckIsTextEditor() {
30926c9bb3a573b236f009654903461e496410a4147Michael Wright        return true;
31026c9bb3a573b236f009654903461e496410a4147Michael Wright    }
31126c9bb3a573b236f009654903461e496410a4147Michael Wright
31226c9bb3a573b236f009654903461e496410a4147Michael Wright    @Override
31326c9bb3a573b236f009654903461e496410a4147Michael Wright    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
31426c9bb3a573b236f009654903461e496410a4147Michael Wright        outAttrs.imeOptions |=
31526c9bb3a573b236f009654903461e496410a4147Michael Wright            EditorInfo.IME_FLAG_NO_EXTRACT_UI |
31626c9bb3a573b236f009654903461e496410a4147Michael Wright            EditorInfo.IME_FLAG_NO_ENTER_ACTION |
31726c9bb3a573b236f009654903461e496410a4147Michael Wright            EditorInfo.IME_ACTION_NONE;
31826c9bb3a573b236f009654903461e496410a4147Michael Wright        outAttrs.inputType = EditorInfo.TYPE_NULL;
31926c9bb3a573b236f009654903461e496410a4147Michael Wright        return new BaseInputConnection(this, false) {
32026c9bb3a573b236f009654903461e496410a4147Michael Wright            @Override
32126c9bb3a573b236f009654903461e496410a4147Michael Wright            public boolean deleteSurroundingText (int leftLength, int rightLength) {
32226c9bb3a573b236f009654903461e496410a4147Michael Wright                KeyEvent k;
32326c9bb3a573b236f009654903461e496410a4147Michael Wright                if (rightLength == 0 && leftLength == 0) {
32426c9bb3a573b236f009654903461e496410a4147Michael Wright                    k = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL);
32526c9bb3a573b236f009654903461e496410a4147Michael Wright                    return this.sendKeyEvent(k);
32626c9bb3a573b236f009654903461e496410a4147Michael Wright                }
32726c9bb3a573b236f009654903461e496410a4147Michael Wright                for (int i = 0; i < leftLength; i++) {
32826c9bb3a573b236f009654903461e496410a4147Michael Wright                    k = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL);
32926c9bb3a573b236f009654903461e496410a4147Michael Wright                    this.sendKeyEvent(k);
33026c9bb3a573b236f009654903461e496410a4147Michael Wright                }
33126c9bb3a573b236f009654903461e496410a4147Michael Wright                for (int i = 0; i < rightLength; i++) {
33226c9bb3a573b236f009654903461e496410a4147Michael Wright                    k = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_FORWARD_DEL);
33326c9bb3a573b236f009654903461e496410a4147Michael Wright                    this.sendKeyEvent(k);
33426c9bb3a573b236f009654903461e496410a4147Michael Wright                }
33526c9bb3a573b236f009654903461e496410a4147Michael Wright                return true;
33626c9bb3a573b236f009654903461e496410a4147Michael Wright            }
33726c9bb3a573b236f009654903461e496410a4147Michael Wright        };
33826c9bb3a573b236f009654903461e496410a4147Michael Wright    }
339410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey}
340