ng.uavp.ch.ngusbterminal.ShellFragment.java Source code

Java tutorial

Introduction

Here is the source code for ng.uavp.ch.ngusbterminal.ShellFragment.java

Source

/*
 * NGUSBTerminal - The Next Generation Multicopter Android Terminal
 * Copyright (C) 2015 by the UAVP-NG Project,
 *     Christian Bergmann <christi@dev.uavp.ch>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * You can find our website at <http://ng.uavp.ch>.
 *
 * Many people helped and are helping developing NGOS. Please
 * have a look at <http://ng.uavp.ch/moin/Authors> for details.
 */

package ng.uavp.ch.ngusbterminal;

import android.support.v4.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.Editable;
import android.text.Selection;
import android.text.method.ArrowKeyMovementMethod;
import android.text.method.MovementMethod;
import android.util.AttributeSet;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionWrapper;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
import ng.uavp.ch.ngusbterminal.MainActivity.ISerialSend;
import ng.uavp.ch.ngusbterminal.MainActivity.ISerialReceive;

public class ShellFragment extends Fragment implements ISerialReceive {
    TerminalEditText terminalView;
    ISerialSend serial;
    Handler recvHandler;

    public ShellFragment(ISerialSend serial) {
        super();
        this.serial = serial;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.shell, container, false);

        terminalView = (TerminalEditText) view.findViewById(R.id.editText1);
        terminalView.HookSerialDevice(serial);
        recvHandler = new Handler(Looper.getMainLooper()) {
            @Override
            public void handleMessage(Message inputMessage) {
                byte[] receivedData = (byte[]) inputMessage.obj;
                terminalView.OnReceived(receivedData);
            }
        };

        if (terminalView.requestFocus()) {
            InputMethodManager imm = (InputMethodManager) getActivity()
                    .getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.showSoftInput(terminalView, InputMethodManager.SHOW_IMPLICIT);
        }

        return view;
    }

    public void setText(String text) {
        terminalView.setText(text);
    }

    public void HookSerialDevice(ISerialSend serial) {
        terminalView.HookSerialDevice(serial);
    }

    public static class TerminalEditText extends TextView {
        ISerialSend usb;
        int escseq = 0;
        int cursorLeft = 0;
        TerminalInputConnection inputConnection;

        public TerminalEditText(Context context) {
            this(context, null);
        }

        public TerminalEditText(Context context, AttributeSet attrs) {
            super(context, attrs);
            setFocusableInTouchMode(true);
        }

        public TerminalEditText(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }

        @Override
        protected MovementMethod getDefaultMovementMethod() {
            return ArrowKeyMovementMethod.getInstance();
        }

        @Override
        public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
            return new TerminalInputConnection(super.onCreateInputConnection(outAttrs), true);
        }

        @Override
        public Editable getText() {
            return (Editable) super.getText();
        }

        public void setSelection(int pos) {
            Selection.setSelection(getText(), pos);
        }

        public void setSelection(int start, int end) {
            Selection.setSelection(getText(), start, end);
        }

        private class TerminalInputConnection extends InputConnectionWrapper {

            public TerminalInputConnection(InputConnection target, boolean mutable) {
                super(target, mutable);
                inputConnection = this;
            }

            // capture normal keys
            @Override
            public boolean commitText(CharSequence text, int newCursorPosition) {
                usb.sendText(text);
                return false;
            }

            // capture special keys
            @Override
            public boolean sendKeyEvent(KeyEvent event) {
                if (event.getAction() == KeyEvent.ACTION_DOWN) {
                    switch (event.getKeyCode()) {
                    case KeyEvent.KEYCODE_ENTER:
                        usb.sendText(MainActivity.NEWLINE);
                        break;

                    case KeyEvent.KEYCODE_DEL:
                        usb.sendText(String.valueOf((char) 0x08));
                        break;

                    case KeyEvent.KEYCODE_DPAD_UP:
                        usb.sendText(String.valueOf((char) 0x1b));
                        usb.sendText(String.valueOf('['));
                        usb.sendText(String.valueOf('A'));
                        break;

                    case KeyEvent.KEYCODE_DPAD_DOWN:
                        usb.sendText(String.valueOf(0x1b));
                        usb.sendText(String.valueOf('['));
                        usb.sendText(String.valueOf('B'));
                        break;

                    case KeyEvent.KEYCODE_DPAD_RIGHT:
                        usb.sendText(String.valueOf((char) 0x1b));
                        usb.sendText(String.valueOf('['));
                        usb.sendText(String.valueOf('C'));
                        break;

                    case KeyEvent.KEYCODE_DPAD_LEFT:
                        usb.sendText(String.valueOf((char) 0x1b));
                        usb.sendText(String.valueOf('['));
                        usb.sendText(String.valueOf('D'));
                        break;

                    default:
                        return true;
                    }
                } else {
                    return true;
                }
                return false;
            }

            public void sendKeyToView(int keycode) {
                super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keycode));
            }

            public void sendKeyEvents(String text) {
                KeyCharacterMap mKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);

                KeyEvent[] events = mKeyCharacterMap.getEvents(text.toCharArray());

                for (KeyEvent event2 : events) {
                    super.sendKeyEvent(new KeyEvent(event2.getAction(), event2.getKeyCode()));
                }
            }
        }

        public void HookSerialDevice(ISerialSend serial) {
            usb = serial;
        }

        @Override
        public void setText(CharSequence text, BufferType type) {
            super.setText(text, type);
            setSelection(text.length());
        }

        int npos = 0;
        boolean got_r = false;

        public void OnReceived(byte[] data) {
            StringBuilder str = new StringBuilder();
            for (int i = 0; i < data.length; i++) {
                switch (data[i]) {
                case 0x1b: // ESC sequence
                    escseq = 1;
                    break;

                case '\n':
                    str.append((char) data[i]);
                    npos = length() + str.length();
                    got_r = false;
                    for (; cursorLeft > 0; cursorLeft--)
                        inputConnection.sendKeyToView(KeyEvent.KEYCODE_DPAD_RIGHT);

                    break;

                case '\r':
                    got_r = true;
                    break;

                default:
                    if (escseq > 0) {
                        escseq++;
                        if (escseq == 3) {
                            if (data[i] == 'D') {
                                cursorLeft++;
                                inputConnection.sendKeyEvents(str.toString());
                                str.delete(0, str.length());
                                inputConnection.sendKeyToView(KeyEvent.KEYCODE_DPAD_LEFT);
                            }
                            if (data[i] == 'C' && cursorLeft > 0) {
                                cursorLeft--;
                                inputConnection.sendKeyEvents(str.toString());
                                str.delete(0, str.length());
                                inputConnection.sendKeyToView(KeyEvent.KEYCODE_DPAD_RIGHT);
                            }
                            escseq = 0;
                        }
                    } else if (got_r) {
                        int strstart = npos - length();
                        if (strstart >= 0) {
                            if (strstart < str.length() - 1)
                                str.delete(strstart, str.length() - 1);
                        } else {
                            str.delete(0, str.length());

                            if (npos < length())
                                // setText(getText().subSequence(0, npos));
                                str.append("\n"); // append Text because
                                                  // replacing is too slow
                        }
                        got_r = false;
                        str.append((char) data[i]);
                    } else {
                        for (; cursorLeft > 0; cursorLeft--) {
                            if (str.length() > 0)
                                str.deleteCharAt(str.length() - 1);
                            else {
                                inputConnection.sendKeyToView(KeyEvent.KEYCODE_FORWARD_DEL);
                            }
                        }
                        str.append((char) data[i]);
                    }
                    break;
                }
            }

            if (cursorLeft == 0) {
                // sending single key events is too slow, so append text if possible
                append(str.toString());
                setSelection(length());
            } else {
                // we have to send single key events for cursor key handling 
                inputConnection.sendKeyEvents(str.toString());
            }
        }
    }

    @Override
    public void OnReceive(byte[] data) {
        // Send serial data to GUI thread
        Message msg = recvHandler.obtainMessage(0, data);
        recvHandler.sendMessage(msg);
    }
}