jackpal.androidterm.Term.java Source code

Java tutorial

Introduction

Here is the source code for jackpal.androidterm.Term.java

Source

/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package jackpal.androidterm;

import android.Manifest;
import android.support.v4.content.ContextCompat;
import android.text.TextUtils;

import jackpal.androidterm.autorun.CommandListActivity;
import jackpal.androidterm.compat.ActionBarCompat;
import jackpal.androidterm.compat.ActivityCompat;
import jackpal.androidterm.compat.AndroidCompat;
import jackpal.androidterm.compat.MenuItemCompat;
import jackpal.androidterm.emulatorview.EmulatorView;
import jackpal.androidterm.emulatorview.TermSession;
import jackpal.androidterm.emulatorview.UpdateCallback;
import jackpal.androidterm.emulatorview.compat.ClipboardManagerCompat;
import jackpal.androidterm.emulatorview.compat.ClipboardManagerCompatFactory;
import jackpal.androidterm.emulatorview.compat.KeycodeConstants;
import jackpal.androidterm.util.SessionList;
import jackpal.androidterm.util.TermSettings;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.text.Collator;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
import android.preference.PreferenceManager;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.webkit.MimeTypeMap;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

/**
 * A terminal emulator activity.
 */

public class Term extends Activity
        implements UpdateCallback, SharedPreferences.OnSharedPreferenceChangeListener, OnClickListener {
    /**
     * The ViewFlipper which holds the collection of EmulatorView widgets.
     */
    private TermViewFlipper mViewFlipper;

    /**
     * The name of the ViewFlipper in the resources.
     */
    private static final int VIEW_FLIPPER = R.id.view_flipper;

    private SessionList mTermSessions;

    private TermSettings mSettings;

    private final static int SELECT_TEXT_ID = 0;
    private final static int COPY_ALL_ID = 1;
    private final static int PASTE_ID = 2;
    private final static int SEND_CONTROL_KEY_ID = 3;
    private final static int SEND_FN_KEY_ID = 4;
    private final static int SEND_FUNCTION_BAR_ID = 5;
    private final static int SEND_MENU_ID = 6;

    private boolean mAlreadyStarted = false;
    private boolean mStopServiceOnFinish = false;

    private Intent TSIntent;

    public static final int REQUEST_CHOOSE_WINDOW = 1;
    public static final String EXTRA_WINDOW_ID = "jackpal.androidterm.window_id";
    private int onResumeSelectWindow = -1;
    private String onResumeSelectWindowHandle;
    private ComponentName mPrivateAlias;

    private PowerManager.WakeLock mWakeLock;
    private WifiManager.WifiLock mWifiLock;
    // Available on API 12 and later
    private static final int WIFI_MODE_FULL_HIGH_PERF = 3;

    private boolean mBackKeyPressed;

    private static final String ACTION_PATH_BROADCAST = "jackpal.androidterm.broadcast.APPEND_TO_PATH";
    private static final String ACTION_PATH_PREPEND_BROADCAST = "jackpal.androidterm.broadcast.PREPEND_TO_PATH";
    private static final String PERMISSION_PATH_BROADCAST = "jackpal.androidterm.permission.APPEND_TO_PATH";
    private static final String PERMISSION_PATH_PREPEND_BROADCAST = "jackpal.androidterm.permission.PREPEND_TO_PATH";
    private int mPendingPathBroadcasts = 0;
    private BroadcastReceiver mPathReceiver;
    // Available on API 12 and later
    private static final int FLAG_INCLUDE_STOPPED_PACKAGES = 0x20;

    private TermService mTermService;
    private ServiceConnection mTSConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            Log.i(TermDebug.LOG_TAG, "Bound to TermService");
            TermService.TSBinder binder = (TermService.TSBinder) service;
            mTermService = binder.getService();
            if (mPendingPathBroadcasts <= 0) {
                populateViewFlipper();
                populateWindowList();
            }
        }

        public void onServiceDisconnected(ComponentName arg0) {
            mTermService = null;
        }
    };

    private ActionBarCompat mActionBar;
    private int mActionBarMode = TermSettings.ACTION_BAR_MODE_NONE;

    private WindowListAdapter mWinListAdapter;

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
        mSettings.readPrefs(sharedPreferences);
    }

    private class WindowListActionBarAdapter extends WindowListAdapter implements UpdateCallback {
        // From android.R.style in API 13
        private static final int TextAppearance_Holo_Widget_ActionBar_Title = 0x01030112;

        public WindowListActionBarAdapter(SessionList sessions) {
            super(sessions);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            TextView label = new TextView(Term.this);
            String title = getSessionTitle(position, getString(R.string.window_title, position + 1));
            label.setText(title);
            if (AndroidCompat.SDK >= 13) {
                label.setTextAppearance(Term.this, TextAppearance_Holo_Widget_ActionBar_Title);
            } else {
                label.setTextAppearance(Term.this, android.R.style.TextAppearance_Medium);
            }
            return label;
        }

        @Override
        public View getDropDownView(int position, View convertView, ViewGroup parent) {
            return super.getView(position, convertView, parent);
        }

        public void onUpdate() {
            notifyDataSetChanged();
            mActionBar.setSelectedNavigationItem(mViewFlipper.getDisplayedChild());
        }
    }

    private ActionBarCompat.OnNavigationListener mWinListItemSelected = new ActionBarCompat.OnNavigationListener() {
        public boolean onNavigationItemSelected(int position, long id) {
            int oldPosition = mViewFlipper.getDisplayedChild();
            if (position != oldPosition) {
                if (position >= mViewFlipper.getChildCount()) {
                    mViewFlipper.addView(createEmulatorView(mTermSessions.get(position)));
                }
                mViewFlipper.setDisplayedChild(position);
                if (mActionBarMode >= TermSettings.ACTION_BAR_MODE_HIDES) {
                    mActionBar.hide();
                }
            }
            return true;
        }
    };

    private boolean mHaveFullHwKeyboard = false;

    private class EmulatorViewGestureListener extends SimpleOnGestureListener {
        private EmulatorView view;

        public EmulatorViewGestureListener(EmulatorView view) {
            this.view = view;
        }

        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            // Let the EmulatorView handle taps if mouse tracking is active
            if (view.isMouseTrackingActive())
                return false;

            //Check for link at tap location
            String link = view.getURLat(e.getX(), e.getY());
            if (link != null)
                execURL(link);
            else
                doUIToggle((int) e.getX(), (int) e.getY(), view.getVisibleWidth(), view.getVisibleHeight());
            return true;
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            float absVelocityX = Math.abs(velocityX);
            float absVelocityY = Math.abs(velocityY);
            if (absVelocityX > Math.max(1000.0f, 2.0 * absVelocityY)) {
                // Assume user wanted side to side movement
                if (velocityX > 0) {
                    // Left to right swipe -- previous window
                    mViewFlipper.showPrevious();
                } else {
                    // Right to left swipe -- next window
                    mViewFlipper.showNext();
                }
                return true;
            } else {
                return false;
            }
        }
    }

    /**
     * Should we use keyboard shortcuts?
     */
    private boolean mUseKeyboardShortcuts;

    /**
     * Intercepts keys before the view/terminal gets it.
     */
    private View.OnKeyListener mKeyListener = new View.OnKeyListener() {
        public boolean onKey(View v, int keyCode, KeyEvent event) {
            return backkeyInterceptor(keyCode, event) || keyboardShortcuts(keyCode, event);
        }

        /**
         * Keyboard shortcuts (tab management, paste)
         */
        private boolean keyboardShortcuts(int keyCode, KeyEvent event) {
            if (event.getAction() != KeyEvent.ACTION_DOWN) {
                return false;
            }
            if (!mUseKeyboardShortcuts) {
                return false;
            }
            boolean isCtrlPressed = (event.getMetaState() & KeycodeConstants.META_CTRL_ON) != 0;
            boolean isShiftPressed = (event.getMetaState() & KeycodeConstants.META_SHIFT_ON) != 0;

            if (keyCode == KeycodeConstants.KEYCODE_TAB && isCtrlPressed) {
                if (isShiftPressed) {
                    mViewFlipper.showPrevious();
                } else {
                    mViewFlipper.showNext();
                }

                return true;
            } else if (keyCode == KeycodeConstants.KEYCODE_N && isCtrlPressed && isShiftPressed) {
                doCreateNewWindow();

                return true;
            } else if (keyCode == KeycodeConstants.KEYCODE_V && isCtrlPressed && isShiftPressed) {
                doPaste();

                return true;
            } else {
                return false;
            }
        }

        /**
         * Make sure the back button always leaves the application.
         */
        private boolean backkeyInterceptor(int keyCode, KeyEvent event) {
            if (keyCode == KeyEvent.KEYCODE_BACK && mActionBarMode >= TermSettings.ACTION_BAR_MODE_HIDES
                    && mActionBar != null && mActionBar.isShowing()) {
                /* We need to intercept the key event before the view sees it,
                   otherwise the view will handle it before we get it */
                onKeyUp(keyCode, event);
                return true;
            } else {
                return false;
            }
        }
    };

    private Handler mHandler = new Handler();

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        Log.v(TermDebug.LOG_TAG, "onCreate");

        mPrivateAlias = new ComponentName(this, RemoteInterface.PRIVACT_ACTIVITY_ALIAS);

        if (icicle == null)
            onNewIntent(getIntent());

        final SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
        mSettings = new TermSettings(getResources(), mPrefs);
        mPrefs.registerOnSharedPreferenceChangeListener(this);

        boolean vimflavor = this.getPackageName().matches(".*vim.androidterm.*");

        if (!vimflavor && mSettings.doPathExtensions()) {
            mPathReceiver = new BroadcastReceiver() {
                public void onReceive(Context context, Intent intent) {
                    String path = makePathFromBundle(getResultExtras(false));
                    if (intent.getAction().equals(ACTION_PATH_PREPEND_BROADCAST)) {
                        mSettings.setPrependPath(path);
                    } else {
                        mSettings.setAppendPath(path);
                    }
                    mPendingPathBroadcasts--;

                    if (mPendingPathBroadcasts <= 0 && mTermService != null) {
                        populateViewFlipper();
                        populateWindowList();
                    }
                }
            };

            Intent broadcast = new Intent(ACTION_PATH_BROADCAST);
            if (AndroidCompat.SDK >= 12) {
                broadcast.addFlags(FLAG_INCLUDE_STOPPED_PACKAGES);
            }
            mPendingPathBroadcasts++;
            sendOrderedBroadcast(broadcast, PERMISSION_PATH_BROADCAST, mPathReceiver, null, RESULT_OK, null, null);

            if (mSettings.allowPathPrepend()) {
                broadcast = new Intent(broadcast);
                broadcast.setAction(ACTION_PATH_PREPEND_BROADCAST);
                mPendingPathBroadcasts++;
                sendOrderedBroadcast(broadcast, PERMISSION_PATH_PREPEND_BROADCAST, mPathReceiver, null, RESULT_OK,
                        null, null);
            }
        }

        TSIntent = new Intent(this, TermService.class);
        startService(TSIntent);

        if (AndroidCompat.SDK >= 11) {
            int theme = mSettings.getColorTheme();
            int actionBarMode = mSettings.actionBarMode();
            mActionBarMode = actionBarMode;
            switch (actionBarMode) {
            case TermSettings.ACTION_BAR_MODE_ALWAYS_VISIBLE:
                if (theme == 0) {
                    setTheme(R.style.Theme_Holo);
                } else {
                    setTheme(R.style.Theme_Holo_Light);
                }
                break;
            case TermSettings.ACTION_BAR_MODE_HIDES + 1:
            case TermSettings.ACTION_BAR_MODE_HIDES:
                if (theme == 0) {
                    setTheme(R.style.Theme_Holo_ActionBarOverlay);
                } else {
                    setTheme(R.style.Theme_Holo_Light_ActionBarOverlay);
                }
                break;
            }
        } else {
            mActionBarMode = TermSettings.ACTION_BAR_MODE_ALWAYS_VISIBLE;
        }

        setContentView(R.layout.term_activity);
        mViewFlipper = (TermViewFlipper) findViewById(VIEW_FLIPPER);
        setFunctionKeyListener();

        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TermDebug.LOG_TAG);
        WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
        int wifiLockMode = WifiManager.WIFI_MODE_FULL;
        if (AndroidCompat.SDK >= 12) {
            wifiLockMode = WIFI_MODE_FULL_HIGH_PERF;
        }
        mWifiLock = wm.createWifiLock(wifiLockMode, TermDebug.LOG_TAG);

        ActionBarCompat actionBar = ActivityCompat.getActionBar(this);
        if (actionBar != null) {
            mActionBar = actionBar;
            actionBar.setNavigationMode(ActionBarCompat.NAVIGATION_MODE_LIST);
            actionBar.setDisplayOptions(0, ActionBarCompat.DISPLAY_SHOW_TITLE);
            if (mActionBarMode >= TermSettings.ACTION_BAR_MODE_HIDES) {
                actionBar.hide();
            }
        }

        mHaveFullHwKeyboard = checkHaveFullHwKeyboard(getResources().getConfiguration());
        setSoftInputMode(mHaveFullHwKeyboard);

        if (mFunctionBar == -1)
            mFunctionBar = mSettings.showFunctionBar() ? 1 : 0;
        if (mFunctionBar == 1)
            setFunctionBar(mFunctionBar);

        updatePrefs();
        permissionCheckExternalStorage();
        mAlreadyStarted = true;
    }

    public static final int REQUEST_STORAGE = 1;

    @SuppressLint("NewApi")
    void permissionCheckExternalStorage() {
        if (AndroidCompat.SDK < 23)
            return;
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, REQUEST_STORAGE);
        } else if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(new String[] { Manifest.permission.READ_EXTERNAL_STORAGE }, REQUEST_STORAGE);
        }
    }

    @Override
    @SuppressLint("NewApi")
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        if (requestCode == REQUEST_STORAGE) {
            if (permissions.equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    if (ContextCompat.checkSelfPermission(this,
                            Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                        requestPermissions(new String[] { Manifest.permission.READ_EXTERNAL_STORAGE },
                                REQUEST_STORAGE);
                    }
                }
            } else if (permissions.equals(Manifest.permission.READ_EXTERNAL_STORAGE)) {
                // do something
            } else {
                //                Toast.makeText(this, "permission does not granted", Toast.LENGTH_SHORT).show();
            }
        } else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

    private String makePathFromBundle(Bundle extras) {
        if (extras == null || extras.size() == 0) {
            return "";
        }

        String[] keys = new String[extras.size()];
        keys = extras.keySet().toArray(keys);
        Collator collator = Collator.getInstance(Locale.US);
        Arrays.sort(keys, collator);

        StringBuilder path = new StringBuilder();
        for (String key : keys) {
            String dir = extras.getString(key);
            if (dir != null && !dir.equals("")) {
                path.append(dir);
                path.append(":");
            }
        }

        return path.substring(0, path.length() - 1);
    }

    @Override
    protected void onStart() {
        super.onStart();

        if (!bindService(TSIntent, mTSConnection, BIND_AUTO_CREATE)) {
            throw new IllegalStateException("Failed to bind to TermService!");
        }
    }

    private void populateViewFlipper() {
        if (mTermService != null) {
            mTermSessions = mTermService.getSessions();

            if (onResumeSelectWindowHandle != null) {
                int i = 0;
                for (TermSession s : mTermSessions) {
                    if (onResumeSelectWindowHandle.equals(((ShellTermSession) s).getHandle())) {
                        onResumeSelectWindow = i;
                        break;
                    }
                    i++;
                }
                onResumeSelectWindowHandle = null;
            }

            if (mTermSessions.size() == 0) {
                try {
                    mTermSessions.add(createTermSession());
                } catch (IOException e) {
                    Toast.makeText(this, "Failed to start terminal session", Toast.LENGTH_LONG).show();
                    finish();
                    return;
                }
            }

            mTermSessions.addCallback(this);

            for (TermSession session : mTermSessions) {
                EmulatorView view = createEmulatorView(session);
                mViewFlipper.addView(view);
            }

            updatePrefs();

            if (onResumeSelectWindow >= 0) {
                mViewFlipper.setDisplayedChild(onResumeSelectWindow);
                onResumeSelectWindow = -1;
            }
            mViewFlipper.onResume();
            if (!mHaveFullHwKeyboard) {
                doShowSoftKeyboard();
            }
        }
    }

    private void populateWindowList() {
        if (mActionBar == null) {
            // Not needed
            return;
        }
        if (mTermSessions != null) {
            int position = mViewFlipper.getDisplayedChild();
            if (mWinListAdapter == null) {
                mWinListAdapter = new WindowListActionBarAdapter(mTermSessions);

                mActionBar.setListNavigationCallbacks(mWinListAdapter, mWinListItemSelected);
            } else {
                mWinListAdapter.setSessions(mTermSessions);
            }
            mViewFlipper.addCallback(mWinListAdapter);

            mActionBar.setSelectedNavigationItem(position);
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(this);

        if (mStopServiceOnFinish) {
            stopService(TSIntent);
            mFunctionBar = -1;
        }
        mTermService = null;
        mTSConnection = null;
        if (mWakeLock.isHeld()) {
            mWakeLock.release();
        }
        if (mWifiLock.isHeld()) {
            mWifiLock.release();
        }
    }

    @SuppressLint("NewApi")
    private void restart() {
        if (AndroidCompat.SDK >= 11) {
            recreate();
        } else {
            finish();
            startActivity(getIntent());
        }
    }

    private TermSession createTermSession() throws IOException {
        TermSettings settings = mSettings;
        TermSession session = new ShellTermSession(settings, getInitialCommand());
        session.setFinishCallback(mTermService);
        return session;
    }

    boolean mFirst = true;

    private String getInitialCommand() {
        String cmd = mSettings.getInitialCommand();
        cmd = mTermService.getInitialCommand(cmd, (mFirst && mTermService.getSessions().size() == 0));
        mFirst = false;
        return cmd;
    }

    static boolean mFirstInputtype = true;

    private TermView createEmulatorView(TermSession session) {
        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
        TermView emulatorView = new TermView(this, session, metrics);

        emulatorView.setExtGestureListener(new EmulatorViewGestureListener(emulatorView));
        emulatorView.setOnKeyListener(mKeyListener);
        registerForContextMenu(emulatorView);

        if (mFirstInputtype) {
            emulatorView.setImeShortcutsAction(mSettings.getmImeDefaultInputtype());
            mFirstInputtype = false;
        }
        return emulatorView;
    }

    private TermSession getCurrentTermSession() {
        SessionList sessions = mTermSessions;
        if (sessions == null) {
            return null;
        } else {
            return sessions.get(mViewFlipper.getDisplayedChild());
        }
    }

    private EmulatorView getCurrentEmulatorView() {
        return (EmulatorView) mViewFlipper.getCurrentView();
    }

    private void updatePrefs() {
        ActivityCompat.invalidateOptionsMenu(this);
        mUseKeyboardShortcuts = mSettings.getUseKeyboardShortcutsFlag();

        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);

        setFunctionKeyVisibility();
        mViewFlipper.updatePrefs(mSettings);

        for (View v : mViewFlipper) {
            ((EmulatorView) v).setDensity(metrics);
            ((TermView) v).updatePrefs(mSettings);
            setPreIMEShortsuts((EmulatorView) v);
            if (mSettings.useCookedIME() == false) {
                ((EmulatorView) v).setIMECtrlBeginBatchEditDisable(false);
            }
        }

        if (mTermSessions != null) {
            for (TermSession session : mTermSessions) {
                ((GenericTermSession) session).updatePrefs(mSettings);
            }
        }

        {
            Window win = getWindow();
            WindowManager.LayoutParams params = win.getAttributes();
            final int FULLSCREEN = WindowManager.LayoutParams.FLAG_FULLSCREEN;
            int desiredFlag = mSettings.showStatusBar() ? 0 : FULLSCREEN;
            if (desiredFlag != (params.flags & FULLSCREEN)
                    || (AndroidCompat.SDK >= 11 && mActionBarMode != mSettings.actionBarMode())) {
                if (mAlreadyStarted) {
                    // Can't switch to/from fullscreen after
                    // starting the activity.
                    restart();
                } else {
                    win.setFlags(desiredFlag, FULLSCREEN);
                    if (mActionBarMode >= TermSettings.ACTION_BAR_MODE_HIDES) {
                        if (mActionBar != null) {
                            mActionBar.hide();
                        }
                    }
                }
            }
        }

        int orientation = mSettings.getScreenOrientation();
        int o = 0;
        if (orientation == 0) {
            o = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
        } else if (orientation == 1) {
            o = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
        } else if (orientation == 2) {
            o = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
        } else {
            /* Shouldn't be happened. */
        }
        setRequestedOrientation(o);
    }

    private void setPreIMEShortsuts(EmulatorView v) {
        final SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
        boolean value = mPrefs.getBoolean("AltGrave", false);
        v.setPreIMEShortcut("AltGrave", value);
        value = mPrefs.getBoolean("AltEsc", false);
        v.setPreIMEShortcut("AltEsc", value);
        value = mPrefs.getBoolean("CtrlSpace", false);
        v.setPreIMEShortcut("CtrlSpace", value);
        value = mPrefs.getBoolean("ZENKAKU_HANKAKU", false);
        v.setPreIMEShortcut("ZENKAKU_HANKAKU", value);
        value = mPrefs.getBoolean("GRAVE", false);
        v.setPreIMEShortcut("GRAVE", value);
        value = mPrefs.getBoolean("SWITCH_CHARSET", false);
        v.setPreIMEShortcut("SWITCH_CHARSET", value);
        value = mPrefs.getBoolean("CtrlJ", false);
        v.setPreIMEShortcut("CtrlJ", value);
        v.setPreIMEShortcutsAction(mSettings.getImeShortcutsAction());
    }

    @Override
    public void onPause() {
        super.onPause();

        if (AndroidCompat.SDK < 5) {
            /* If we lose focus between a back key down and a back key up,
               we shouldn't respond to the next back key up event unless
               we get another key down first */
            mBackKeyPressed = false;
        }

        /* Explicitly close the input method
           Otherwise, the soft keyboard could cover up whatever activity takes
           our place */
        final IBinder token = mViewFlipper.getWindowToken();
        new Thread() {
            @Override
            public void run() {
                InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(token, 0);
            }
        }.start();
    }

    @Override
    protected void onStop() {
        mViewFlipper.onPause();
        if (mTermSessions != null) {
            mTermSessions.removeCallback(this);

            if (mWinListAdapter != null) {
                mTermSessions.removeCallback(mWinListAdapter);
                mTermSessions.removeTitleChangedListener(mWinListAdapter);
                mViewFlipper.removeCallback(mWinListAdapter);
            }
        }

        mViewFlipper.removeAllViews();

        unbindService(mTSConnection);

        super.onStop();
    }

    private boolean checkHaveFullHwKeyboard(Configuration c) {
        return (c.keyboard == Configuration.KEYBOARD_QWERTY)
                && (c.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO);
    }

    private void setSoftInputMode(boolean haveFullHwKeyboard) {
        int mode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
        if (haveFullHwKeyboard) {
            mode |= WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
        } else {
            mode |= WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
        }
        getWindow().setSoftInputMode(mode);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        mHaveFullHwKeyboard = checkHaveFullHwKeyboard(newConfig);
        setSoftInputMode(mHaveFullHwKeyboard);

        EmulatorView v = (EmulatorView) mViewFlipper.getCurrentView();
        if (v != null) {
            v.updateSize(false);
            v.onConfigurationChangedToEmulatorView(newConfig);
        }

        if (mWinListAdapter != null) {
            // Force Android to redraw the label in the navigation dropdown
            mWinListAdapter.notifyDataSetChanged();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        if (mSettings.getActionBarPlusKeyAction() != 999) {
            MenuItemCompat.setShowAsAction(menu.findItem(R.id.menu_plus), MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
        } else {
            menu.removeItem(R.id.menu_plus);
        }
        //        if (mSettings.getActionBarMinusKeyAction() != 999) {
        //            MenuItemCompat.setShowAsAction(menu.findItem(R.id.menu_minus), MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
        //        } else {
        //            menu.removeItem(R.id.menu_minus);
        //        }
        if (mSettings.getActionBarXKeyAction() != 999) {
            MenuItemCompat.setShowAsAction(menu.findItem(R.id.menu_x), MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
        } else {
            menu.removeItem(R.id.menu_x);
        }
        //        if (mSettings.getActionBarUserKeyAction() != 999) {
        //            MenuItemCompat.setShowAsAction(menu.findItem(R.id.menu_user), MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
        //        } else {
        //            menu.removeItem(R.id.menu_user);
        //        }
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        switch (id) {
        case R.id.menu_preferences:
            doPreferences();
            break;
        case R.id.menu_paste:
            doPaste();
            break;
        case R.id.menu_new_window:
            doCreateNewWindow();
            break;
        case R.id.menu_plus: {
            EmulatorView view = getCurrentEmulatorView();
            if (view != null) {
                int key = mSettings.getActionBarPlusKeyAction();
                return doSendActionBarKey(view, key);
            }
            break;
        }
        //            case R.id.menu_minus: {
        //                EmulatorView view = getCurrentEmulatorView();
        //                if (view != null) {
        //                    int key = mSettings.getActionBarMinusKeyAction();
        //                    return doSendActionBarKey(view, key);
        //                }
        //                break;
        //            }
        case R.id.menu_close_window:
            confirmCloseWindow();
            break;
        case R.id.menu_x: {
            EmulatorView view = getCurrentEmulatorView();
            if (view != null) {
                int key = mSettings.getActionBarXKeyAction();
                return doSendActionBarKey(view, key);
            }
            break;
        }
        //            case R.id.menu_user: {
        //                EmulatorView view = getCurrentEmulatorView();
        //                if (view != null) {
        //                    int key = mSettings.getActionBarUserKeyAction();
        //                    return doSendActionBarKey(view, key);
        //                }
        //                break;
        //            }
        case R.id.menu_window_list:
            startActivityForResult(new Intent(this, WindowList.class), REQUEST_CHOOSE_WINDOW);
            break;
        case R.id.menu_reset:
            doResetTerminal();
            Toast toast = Toast.makeText(this, R.string.reset_toast_notification, Toast.LENGTH_LONG);
            toast.setGravity(Gravity.CENTER, 0, 0);
            toast.show();
            break;
        case R.id.menu_send_email:
            doEmailTranscript();
            break;
        case R.id.menu_special_keys:
            doDocumentKeys();
            break;
        case R.id.menu_toggle_soft_keyboard:
            doToggleSoftKeyboard();
            break;
        case R.id.menu_toggle_function_bar:
            setFunctionBar(2);
            break;
        case R.id.menu_toggle_wakelock:
            doToggleWakeLock();
            break;
        case R.id.menu_toggle_wifilock:
            doToggleWifiLock();
            break;
        case R.id.action_help:
            Intent openHelp = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.help_url)));
            startActivity(openHelp);
            break;
        case R.id.menu_scripts:
            startActivity(new Intent(this, CommandListActivity.class));
            break;
        }
        // Hide the action bar if appropriate
        if (mActionBarMode >= TermSettings.ACTION_BAR_MODE_HIDES) {
            mActionBar.hide();
        }
        return super.onOptionsItemSelected(item);
    }

    private static boolean mVimApp = false;

    private boolean doSendActionBarKey(EmulatorView view, int key) {
        if (key == 999) {
            // do nothing
        } else if (key == 1002) {

            doToggleSoftKeyboard();
        } else if (key == 1249) {
            doPaste();
        } else if (key == 1250) {
            doCreateNewWindow();
        } else if (key == 1251) {
            if (mVimApp && mSettings.getInitialCommand().matches("(.|\n)*(^|\n)-vim\\.app(.|\n)*")
                    && mTermSessions.size() == 1) {
                sendKeyStrings(":confirm qa\r", true);
            } else {
                confirmCloseWindow();
            }
        } else if (key == 1252) {
            InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.showInputMethodPicker();
        } else if (key == 1253) {
            sendKeyStrings(":confirm qa\r", true);
        } else if (key == 1254) {
            view.sendFnKeyCode();
        } else if (key == KeycodeConstants.KEYCODE_ALT_LEFT) {
            view.sendAltKeyCode();
        } else if (key == KeycodeConstants.KEYCODE_CTRL_LEFT) {
            view.sendControlKeyCode();
        } else if (key == 1247) {
            sendKeyStrings(":", false);
        } else if (key == 1255) {
            setFunctionBar(2);
        } else if (key == 1260) {
            view.doImeShortcutsAction();
        } else if (key >= 1351 && key <= 1353) {
            view.doImeShortcutsAction(key - 1300);
        } else if (key > 0) {
            KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, key);
            dispatchKeyEvent(event);
            event = new KeyEvent(KeyEvent.ACTION_UP, key);
            dispatchKeyEvent(event);
        }
        return true;
    }

    private void sendKeyStrings(String str, boolean esc) {
        TermSession session = getCurrentTermSession();
        if (session != null) {
            if (esc)
                str = "\u001b" + str;
            session.write(str);
        }
        return;
    }

    private void doCreateNewWindow() {
        if (mTermSessions == null) {
            Log.w(TermDebug.LOG_TAG, "Couldn't create new window because mTermSessions == null");
            return;
        }

        try {
            TermSession session = createTermSession();

            mTermSessions.add(session);

            TermView view = createEmulatorView(session);
            view.updatePrefs(mSettings);

            mViewFlipper.addView(view);
            mViewFlipper.setDisplayedChild(mViewFlipper.getChildCount() - 1);
        } catch (IOException e) {
            Toast.makeText(this, "Failed to create a session", Toast.LENGTH_SHORT).show();
        }
    }

    private void confirmCloseWindow() {
        final AlertDialog.Builder b = new AlertDialog.Builder(this);
        b.setIcon(android.R.drawable.ic_dialog_alert);
        b.setMessage(R.string.confirm_window_close_message);
        final Runnable closeWindow = new Runnable() {
            public void run() {
                doCloseWindow();
            }
        };
        b.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                dialog.dismiss();
                mHandler.post(closeWindow);
            }
        });
        b.setNegativeButton(android.R.string.no, null);
        b.show();
    }

    private void doCloseWindow() {
        if (mTermSessions == null) {
            return;
        }

        EmulatorView view = getCurrentEmulatorView();
        if (view == null) {
            return;
        }
        if (mTermSessions.size() == 1) {
            doHideSoftKeyboard();
        }
        TermSession session = mTermSessions.remove(mViewFlipper.getDisplayedChild());
        view.onPause();
        session.finish();
        mViewFlipper.removeView(view);
        if (mTermSessions.size() != 0) {
            mViewFlipper.showNext();
        }
    }

    @Override
    protected void onActivityResult(int request, int result, Intent data) {
        switch (request) {
        case REQUEST_CHOOSE_WINDOW:
            if (result == RESULT_OK && data != null) {
                int position = data.getIntExtra(EXTRA_WINDOW_ID, -2);
                if (position >= 0) {
                    // Switch windows after session list is in sync, not here
                    onResumeSelectWindow = position;
                } else if (position == -1) {
                    doCreateNewWindow();
                    onResumeSelectWindow = mTermSessions.size() - 1;
                }
            } else {
                // Close the activity if user closed all sessions
                // TODO the left path will be invoked when nothing happened, but this Activity was destroyed!
                if (mTermSessions == null || mTermSessions.size() == 0) {
                    mStopServiceOnFinish = true;
                    finish();
                }
            }
            break;
        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
            // Don't repeat action if intent comes from history
            return;
        }

        String action = intent.getAction();
        if (TextUtils.isEmpty(action) || !mPrivateAlias.equals(intent.getComponent())) {
            return;
        }

        // huge number simply opens new window
        // TODO: add a way to restrict max number of windows per caller (possibly via reusing BoundSession)
        switch (action) {
        case RemoteInterface.PRIVACT_OPEN_NEW_WINDOW:
            onResumeSelectWindow = Integer.MAX_VALUE;
            break;
        case RemoteInterface.PRIVACT_SWITCH_WINDOW:
            int target = intent.getIntExtra(RemoteInterface.PRIVEXTRA_TARGET_WINDOW, -1);
            if (target >= 0) {
                onResumeSelectWindow = target;
            } else {
                onResumeSelectWindowHandle = intent.getStringExtra(RemoteInterface.EXTRA_WINDOW_HANDLE);
            }
            break;
        }
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        MenuItem wakeLockItem = menu.findItem(R.id.menu_toggle_wakelock);
        MenuItem wifiLockItem = menu.findItem(R.id.menu_toggle_wifilock);
        if (mWakeLock.isHeld()) {
            wakeLockItem.setTitle(R.string.disable_wakelock);
        } else {
            wakeLockItem.setTitle(R.string.enable_wakelock);
        }
        if (mWifiLock.isHeld()) {
            wifiLockItem.setTitle(R.string.disable_wifilock);
        } else {
            wifiLockItem.setTitle(R.string.enable_wifilock);
        }
        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        menu.setHeaderTitle(R.string.edit_text);
        menu.add(0, SELECT_TEXT_ID, 0, R.string.select_text);
        menu.add(0, COPY_ALL_ID, 0, R.string.copy_all);
        menu.add(0, PASTE_ID, 0, R.string.paste);
        // menu.add(0, SEND_CONTROL_KEY_ID, 0, R.string.send_control_key);
        // menu.add(0, SEND_FN_KEY_ID, 0, R.string.send_fn_key);
        menu.add(0, SEND_MENU_ID, 0, R.string.title_functionbar_menu);
        // menu.add(0, SEND_FUNCTION_BAR_ID, 0, R.string.toggle_function_bar);
        if (!canPaste()) {
            menu.getItem(PASTE_ID).setEnabled(false);
        }
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case SELECT_TEXT_ID:
            getCurrentEmulatorView().toggleSelectingText();
            return true;
        case COPY_ALL_ID:
            doCopyAll();
            return true;
        case PASTE_ID:
            doPaste();
            return true;
        case SEND_CONTROL_KEY_ID:
            doSendControlKey();
            return true;
        case SEND_FN_KEY_ID:
            doSendFnKey();
            return true;
        case SEND_MENU_ID:
            openOptionsMenu();
            return true;
        case SEND_FUNCTION_BAR_ID:
            setFunctionBar(2);
            return true;
        default:
            return super.onContextItemSelected(item);
        }
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        /* The pre-Eclair default implementation of onKeyDown() would prevent
           our handling of the Back key in onKeyUp() from taking effect, so
           ignore it here */
        if (AndroidCompat.SDK < 5 && keyCode == KeyEvent.KEYCODE_BACK) {
            /* Android pre-Eclair has no key event tracking, and a back key
               down event delivered to an activity above us in the back stack
               could be succeeded by a back key up event to us, so we need to
               keep track of our own back key presses */
            mBackKeyPressed = true;
            return true;
        } else {
            return super.onKeyDown(keyCode, event);
        }
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        switch (keyCode) {
        case 0xfffffffe:
            if (mTermSessions.size() > 1) {
                return true;
            }
            // fall into next
        case 0xffffffff:
            if (mTermSessions.size() == 1) {
                doHideSoftKeyboard();
            }
            doCloseWindow();
            return true;
        case 0xffff0000:
            setFunctionBar(2);
            return true;
        case KeyEvent.KEYCODE_BACK:
            if (AndroidCompat.SDK < 5) {
                if (!mBackKeyPressed) {
                    /* This key up event might correspond to a key down
                       delivered to another activity -- ignore */
                    return false;
                }
                mBackKeyPressed = false;
            }
            if (mActionBarMode >= TermSettings.ACTION_BAR_MODE_HIDES && mActionBar != null
                    && mActionBar.isShowing()) {
                mActionBar.hide();
                return true;
            }
            switch (mSettings.getBackKeyAction()) {
            case TermSettings.BACK_KEY_STOPS_SERVICE:
                mStopServiceOnFinish = true;
            case TermSettings.BACK_KEY_CLOSES_ACTIVITY:
                finish();
                return true;
            case TermSettings.BACK_KEY_CLOSES_WINDOW:
                doCloseWindow();
                return true;
            case TermSettings.BACK_KEY_TOGGLE_IME:
                doToggleSoftKeyboard();
                return true;
            default:
                return false;
            }
        case KeyEvent.KEYCODE_MENU:
            if (mActionBar != null && !mActionBar.isShowing()) {
                mActionBar.show();
                return true;
            } else {
                return super.onKeyUp(keyCode, event);
            }
        case 0xfffffff1:
            copyFileToClipboard(mSettings.getHomePath() + "/.clipboard");
            return true;
        case 0xffffffe0:
            setFunctionBar(0);
            return true;
        case 0xffffffe1:
            setFunctionBar(1);
            return true;
        case 0xfffffff2:
            setFunctionBar(2);
            return true;
        case 0xfffffff3:
            openOptionsMenu();
            return true;
        case 0xfffffff4:
        case 0xfffffff5:
            copyClipboardToFile(mSettings.getHomePath() + "/.clipboard");
            if (keyCode == 0xfffffff5)
                sendKeyStrings(":ATEMod _paste\r", true);
            return true;
        case 0xfffffff6:
        case 0xfffffff7:
            mVimPaste = (keyCode == 0xfffffff6) ? true : false;
            return true;
        case 0xfffffff8:
        case 0xfffffff9:
            mVimApp = (keyCode == 0xfffffff8) ? true : false;
            return true;
        case 0xfffffffa:
            clearClipBoard();
            return true;
        case 0xfffffffb:
            doAndroidIntent(mSettings.getHomePath() + "/.intent");
            return true;
        default:
            return super.onKeyUp(keyCode, event);
        }
    }

    private void doAndroidIntent(String filename) {
        if (filename == null)
            return;
        String str[] = new String[3];
        try {
            File file = new File(filename);
            BufferedReader br = new BufferedReader(new FileReader(file));

            for (int i = 0; i < 3; i++) {
                str[i] = br.readLine();
                if (str[i] == null)
                    break;
            }
            br.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return;
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        String action = str[0];
        if (action == null || str[1] == null)
            return;

        TermSession session = getCurrentTermSession();
        if (session != null) {
            if (action.equalsIgnoreCase("activity")) {
                try {
                    startActivity(new Intent(this, Class.forName(str[1])));
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            } else if (action.matches("^.*(VIEW|EDIT).*")) {
                Intent intent = new Intent(action);

                String MIME;
                if (str[2] != null) {
                    MIME = str[2];
                } else {
                    int ch = str[1].lastIndexOf('.');
                    String ext = (ch >= 0) ? str[1].substring(ch + 1) : null;
                    MIME = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext.toLowerCase());
                    if (MIME != null && !MIME.equals("")) {
                        intent.setType(MIME);
                    } else {
                        Log.e("CreateIntent", "MIME is Error");
                    }
                }
                intent.setDataAndType(Uri.parse(str[1]), MIME);
                startActivity(intent);
            }
        }
    }

    private void copyFileToClipboard(String filename) {
        if (filename == null)
            return;
        FileInputStream fis;
        try {
            fis = new FileInputStream(filename);
            FileChannel fc = fis.getChannel();
            try {
                ByteBuffer bbuf;
                bbuf = fc.map(FileChannel.MapMode.READ_ONLY, 0, (int) fc.size());
                // Create a read-only CharBuffer on the file
                CharBuffer cbuf = Charset.forName("UTF-8").newDecoder().decode(bbuf);
                String str = cbuf.toString();
                ClipboardManagerCompat clip = ClipboardManagerCompatFactory.getManager(getApplicationContext());
                clip.setText(str);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private void copyClipboardToFile(String filename) {
        if (filename == null)
            return;
        FileOutputStream fos;
        try {
            fos = new FileOutputStream(filename);
            FileChannel fc = fos.getChannel();
            try {
                ClipboardManagerCompat clip = ClipboardManagerCompatFactory.getManager(getApplicationContext());
                if (clip.hasText()) {
                    ByteBuffer by = ByteBuffer.wrap(clip.getText().toString().getBytes());
                    fc.write(by);
                }
                fc.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private void clearClipBoard() {
        ClipboardManagerCompat clip = ClipboardManagerCompatFactory.getManager(getApplicationContext());
        clip.setText("");
    }

    // Called when the list of sessions changes
    public void onUpdate() {
        SessionList sessions = mTermSessions;
        if (sessions == null) {
            return;
        }

        if (sessions.size() == 0) {
            mStopServiceOnFinish = true;
            finish();
        } else if (sessions.size() < mViewFlipper.getChildCount()) {
            for (int i = 0; i < mViewFlipper.getChildCount(); ++i) {
                EmulatorView v = (EmulatorView) mViewFlipper.getChildAt(i);
                if (!sessions.contains(v.getTermSession())) {
                    v.onPause();
                    mViewFlipper.removeView(v);
                    --i;
                }
            }
        }
    }

    private boolean canPaste() {
        ClipboardManagerCompat clip = ClipboardManagerCompatFactory.getManager(getApplicationContext());
        if (clip.hasText()) {
            return true;
        }
        return false;
    }

    private void doPreferences() {
        startActivity(new Intent(this, TermPreferences.class));
    }

    private void doResetTerminal() {
        TermSession session = getCurrentTermSession();
        if (session != null) {
            session.reset();
        }
    }

    private void doEmailTranscript() {
        TermSession session = getCurrentTermSession();
        if (session != null) {
            // Don't really want to supply an address, but
            // currently it's required, otherwise nobody
            // wants to handle the intent.
            String addr = "user@example.com";
            Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:" + addr));

            String subject = getString(R.string.email_transcript_subject);
            String title = session.getTitle();
            if (title != null) {
                subject = subject + " - " + title;
            }
            intent.putExtra(Intent.EXTRA_SUBJECT, subject);
            intent.putExtra(Intent.EXTRA_TEXT, session.getTranscriptText().trim());
            try {
                startActivity(Intent.createChooser(intent, getString(R.string.email_transcript_chooser_title)));
            } catch (ActivityNotFoundException e) {
                Toast.makeText(this, R.string.email_transcript_no_email_activity_found, Toast.LENGTH_LONG).show();
            }
        }
    }

    private void doCopyAll() {
        ClipboardManagerCompat clip = ClipboardManagerCompatFactory.getManager(getApplicationContext());
        clip.setText(getCurrentEmulatorView().getTranscriptScreenText());
    }

    private static boolean mVimPaste = false;

    private void doPaste() {
        if (mVimPaste && mSettings.getInitialCommand().matches("(.|\n)*(^|\n)-vim\\.app(.|\n)*")
                && mTermSessions.size() == 1) {
            sendKeyStrings("\"*p", false);
            return;
        }
        if (!canPaste()) {
            return;
        }
        ClipboardManagerCompat clip = ClipboardManagerCompatFactory.getManager(getApplicationContext());
        CharSequence paste = clip.getText();
        getCurrentTermSession().write(paste.toString());
    }

    private void doSendControlKey() {
        getCurrentEmulatorView().sendControlKey();
    }

    private void doSendFnKey() {
        getCurrentEmulatorView().sendFnKey();
    }

    private void doDocumentKeys() {
        AlertDialog.Builder dialog = new AlertDialog.Builder(this);
        Resources r = getResources();
        dialog.setTitle(r.getString(R.string.control_key_dialog_title));
        dialog.setMessage(formatMessage(mSettings.getControlKeyId(), TermSettings.CONTROL_KEY_ID_NONE, r,
                R.array.control_keys_short_names, R.string.control_key_dialog_control_text,
                R.string.control_key_dialog_control_disabled_text, "CTRLKEY")
                + "\n\n"
                + formatMessage(mSettings.getFnKeyId(), TermSettings.FN_KEY_ID_NONE, r, R.array.fn_keys_short_names,
                        R.string.control_key_dialog_fn_text, R.string.control_key_dialog_fn_disabled_text,
                        "FNKEY"));
        dialog.show();
    }

    private String formatMessage(int keyId, int disabledKeyId, Resources r, int arrayId, int enabledId,
            int disabledId, String regex) {
        if (keyId == disabledKeyId) {
            return r.getString(disabledId);
        }
        String[] keyNames = r.getStringArray(arrayId);
        String keyName = keyNames[keyId];
        String template = r.getString(enabledId);
        String result = template.replaceAll(regex, keyName);
        return result;
    }

    private void doToggleSoftKeyboard() {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);

    }

    private void doShowSoftKeyboard() {
        if (getCurrentEmulatorView() == null)
            return;
        Activity activity = this;
        InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.showSoftInput(getCurrentEmulatorView(), InputMethodManager.SHOW_FORCED);
    }

    private void doHideSoftKeyboard() {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
    }

    private void doToggleWakeLock() {
        if (mWakeLock.isHeld()) {
            mWakeLock.release();
        } else {
            mWakeLock.acquire();
        }
        ActivityCompat.invalidateOptionsMenu(this);
    }

    private void doToggleWifiLock() {
        if (mWifiLock.isHeld()) {
            mWifiLock.release();
        } else {
            mWifiLock.acquire();
        }
        ActivityCompat.invalidateOptionsMenu(this);
    }

    private void doToggleActionBar() {
        ActionBarCompat bar = mActionBar;
        if (bar == null) {
            return;
        }
        if (bar.isShowing()) {
            bar.hide();
        } else {
            bar.show();
        }
    }

    private void doUIToggle(int x, int y, int width, int height) {
        switch (mActionBarMode) {
        case TermSettings.ACTION_BAR_MODE_NONE:
            if (AndroidCompat.SDK >= 11 && (mHaveFullHwKeyboard || y < height / 2)) {
                openOptionsMenu();
                return;
            } else {
                doToggleSoftKeyboard();
            }
            break;
        case TermSettings.ACTION_BAR_MODE_HIDES + 1:
        case TermSettings.ACTION_BAR_MODE_ALWAYS_VISIBLE:
            if (!mHaveFullHwKeyboard) {
                doToggleSoftKeyboard();
            }
            break;
        case TermSettings.ACTION_BAR_MODE_HIDES:
            if (mHaveFullHwKeyboard || y < height / 2) {
                doToggleActionBar();
                return;
            } else {
                doToggleSoftKeyboard();
            }
            break;
        }
        getCurrentEmulatorView().requestFocus();
    }

    /**
     *
     * Send a URL up to Android to be handled by a browser.
     * @param link The URL to be opened.
     */
    private void execURL(String link) {
        Uri webLink = Uri.parse(link);
        Intent openLink = new Intent(Intent.ACTION_VIEW, webLink);
        PackageManager pm = getPackageManager();
        List<ResolveInfo> handlers = pm.queryIntentActivities(openLink, 0);
        if (handlers.size() > 0)
            startActivity(openLink);
    }

    public boolean setDevBoolean(Context context, String key, boolean value) {
        SharedPreferences pref = context.getSharedPreferences("dev", Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = pref.edit();
        editor.putBoolean(key, value);
        editor.commit();
        return value;
    }

    public boolean getDevBoolean(Context context, String key, boolean defValue) {
        SharedPreferences pref = context.getSharedPreferences("dev", Context.MODE_PRIVATE);
        return pref.getBoolean(key, defValue);
    }

    private static int mFunctionBar = -1;

    private void setFunctionBar(int mode) {
        if (mode == 2)
            mFunctionBar = mFunctionBar == 0 ? 1 : 0;
        else
            mFunctionBar = mode;
        if (mAlreadyStarted)
            updatePrefs();
    }

    private void setFunctionBarSize() {
        int size = findViewById(R.id.view_function_bar).getHeight();
        if (mViewFlipper != null)
            mViewFlipper.setFunctionBarSize(size);
    }

    private void setFunctionKeyListener() {
        findViewById(R.id.button_esc).setOnClickListener(this);
        findViewById(R.id.button_ctrl).setOnClickListener(this);
        findViewById(R.id.button_alt).setOnClickListener(this);
        findViewById(R.id.button_tab).setOnClickListener(this);
        findViewById(R.id.button_up).setOnClickListener(this);
        findViewById(R.id.button_down).setOnClickListener(this);
        findViewById(R.id.button_left).setOnClickListener(this);
        findViewById(R.id.button_right).setOnClickListener(this);
        findViewById(R.id.button_backspace).setOnClickListener(this);
        findViewById(R.id.button_enter).setOnClickListener(this);
        findViewById(R.id.button_i).setOnClickListener(this);
        findViewById(R.id.button_colon).setOnClickListener(this);
        findViewById(R.id.button_slash).setOnClickListener(this);
        findViewById(R.id.button_equal).setOnClickListener(this);
        findViewById(R.id.button_asterisk).setOnClickListener(this);
        findViewById(R.id.button_pipe).setOnClickListener(this);
        findViewById(R.id.button_minus).setOnClickListener(this);
        findViewById(R.id.button_vim_paste).setOnClickListener(this);
        findViewById(R.id.button_vim_yank).setOnClickListener(this);
        findViewById(R.id.button_softkeyboard).setOnClickListener(this);
        findViewById(R.id.button_menu).setOnClickListener(this);
        findViewById(R.id.button_menu_hide).setOnClickListener(this);
        findViewById(R.id.button_menu_plus).setOnClickListener(this);
        findViewById(R.id.button_menu_minus).setOnClickListener(this);
        findViewById(R.id.button_menu_x).setOnClickListener(this);
        findViewById(R.id.button_menu_user).setOnClickListener(this);
        findViewById(R.id.button_menu_quit).setOnClickListener(this);
    }

    private void setFunctionKeyVisibility() {
        int visibility;
        final SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(this);

        visibility = mPrefs.getBoolean("functionbar_esc", true) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_esc, visibility);
        visibility = mPrefs.getBoolean("functionbar_ctrl", true) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_ctrl, visibility);
        visibility = mPrefs.getBoolean("functionbar_alt", false) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_alt, visibility);
        visibility = mPrefs.getBoolean("functionbar_tab", true) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_tab, visibility);

        visibility = mPrefs.getBoolean("functionbar_up", true) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_up, visibility);
        visibility = mPrefs.getBoolean("functionbar_down", true) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_down, visibility);
        visibility = mPrefs.getBoolean("functionbar_left", false) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_left, visibility);
        visibility = mPrefs.getBoolean("functionbar_right", false) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_right, visibility);

        visibility = mPrefs.getBoolean("functionbar_backspace", false) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_backspace, visibility);
        visibility = mPrefs.getBoolean("functionbar_enter", false) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_enter, visibility);

        visibility = mPrefs.getBoolean("functionbar_i", false) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_i, visibility);
        visibility = mPrefs.getBoolean("functionbar_colon", false) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_colon, visibility);
        visibility = mPrefs.getBoolean("functionbar_slash", false) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_slash, visibility);
        visibility = mPrefs.getBoolean("functionbar_equal", false) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_equal, visibility);
        visibility = mPrefs.getBoolean("functionbar_asterisk", false) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_asterisk, visibility);
        visibility = mPrefs.getBoolean("functionbar_pipe", false) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_pipe, visibility);
        visibility = mPrefs.getBoolean("functionbar_minus", false) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_minus, visibility);
        visibility = mPrefs.getBoolean("functionbar_vim_paste", false) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_vim_paste, visibility);
        visibility = mPrefs.getBoolean("functionbar_vim_yank", false) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_vim_yank, visibility);

        visibility = mPrefs.getBoolean("functionbar_menu", true) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_menu, visibility);
        visibility = mPrefs.getBoolean("functionbar_softkeyboard", true) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_softkeyboard, visibility);
        visibility = mPrefs.getBoolean("functionbar_hide", true) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_menu_hide, visibility);

        visibility = View.GONE;
        // visibility = mPrefs.getBoolean("functionbar_menu_plus", false)  ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_menu_plus, visibility);
        // visibility = mPrefs.getBoolean("functionbar_menu_minus", false) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_menu_minus, visibility);
        // visibility = mPrefs.getBoolean("functionbar_menu_x", false) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_menu_x, visibility);
        // visibility = mPrefs.getBoolean("functionbar_menu_user", false)  ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_menu_user, visibility);
        visibility = mPrefs.getBoolean("functionbar_menu_quit", true) ? View.VISIBLE : View.GONE;
        setFunctionBarButton(R.id.button_menu_quit, visibility);

        setFunctionBarSize();
        visibility = mFunctionBar == 1 ? View.VISIBLE : View.GONE;
        findViewById(R.id.view_function_bar).setVisibility(visibility);
        mViewFlipper.setFunctionBar(mFunctionBar == 1);
    }

    @SuppressLint("NewApi")
    private void setFunctionBarButton(int id, int visibility) {
        Button button = (Button) findViewById(id);
        button.setVisibility(visibility);
        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
        int height = mSettings.getFontSize() * (int) (metrics.density * metrics.scaledDensity);
        button.setMinHeight(height);
        if (AndroidCompat.SDK >= 14) {
            button.setAllCaps(false);
        }
    }

    public void onClick(View v) {
        EmulatorView view = getCurrentEmulatorView();
        switch (v.getId()) {
        case R.id.button_esc:
            doSendActionBarKey(view, KeycodeConstants.KEYCODE_ESCAPE);
            break;
        case R.id.button_ctrl:
            doSendActionBarKey(view, KeycodeConstants.KEYCODE_CTRL_LEFT);
            break;
        case R.id.button_alt:
            doSendActionBarKey(view, KeycodeConstants.KEYCODE_ALT_LEFT);
            break;
        case R.id.button_tab:
            doSendActionBarKey(view, KeycodeConstants.KEYCODE_TAB);
            break;
        case R.id.button_up:
            doSendActionBarKey(view, KeycodeConstants.KEYCODE_DPAD_UP);
            break;
        case R.id.button_down:
            doSendActionBarKey(view, KeycodeConstants.KEYCODE_DPAD_DOWN);
            break;
        case R.id.button_left:
            doSendActionBarKey(view, KeycodeConstants.KEYCODE_DPAD_LEFT);
            break;
        case R.id.button_right:
            doSendActionBarKey(view, KeycodeConstants.KEYCODE_DPAD_RIGHT);
            break;
        case R.id.button_backspace:
            doSendActionBarKey(view, KeycodeConstants.KEYCODE_DEL);
            break;
        case R.id.button_enter:
            doSendActionBarKey(view, KeycodeConstants.KEYCODE_ENTER);
            break;
        case R.id.button_i:
            sendKeyStrings("i", false);
            break;
        case R.id.button_colon:
            sendKeyStrings(":", false);
            break;
        case R.id.button_slash:
            sendKeyStrings("/", false);
            break;
        case R.id.button_equal:
            sendKeyStrings("=", false);
            break;
        case R.id.button_asterisk:
            sendKeyStrings("*", false);
            break;
        case R.id.button_pipe:
            sendKeyStrings("|", false);
            break;
        case R.id.button_minus:
            sendKeyStrings("-", false);
            break;
        case R.id.button_vim_paste:
            sendKeyStrings("\"*p", false);
            break;
        case R.id.button_vim_yank:
            sendKeyStrings("\"*yy", false);
            break;
        case R.id.button_menu_plus:
            doSendActionBarKey(view, mSettings.getActionBarPlusKeyAction());
            break;
        case R.id.button_menu_minus:
            doSendActionBarKey(view, mSettings.getActionBarMinusKeyAction());
            break;
        case R.id.button_menu_x:
            doSendActionBarKey(view, mSettings.getActionBarXKeyAction());
            break;
        case R.id.button_menu_user:
            doSendActionBarKey(view, mSettings.getActionBarUserKeyAction());
            break;
        case R.id.button_menu_quit:
            doSendActionBarKey(view, mSettings.getActionBarQuitKeyAction());
            break;
        case R.id.button_softkeyboard:
            doSendActionBarKey(view, mSettings.getActionBarIconKeyAction());
            break;
        case R.id.button_menu:
            openOptionsMenu();
            break;
        case R.id.button_menu_hide:
            setFunctionBar(2);
            break;
        }
    }

}