/*
* 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 com.adblog.term;
import jackpal.androidterm.emulatorview.ColorScheme;
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 java.io.UnsupportedEncodingException;
import java.text.Collator;
import java.util.Arrays;
import java.util.Locale;
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.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.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
import android.widget.Toast;
import com.adblog.compat.ActionBarCompat;
import com.adblog.compat.ActivityCompat;
import com.adblog.compat.AndroidCompat;
import com.adblog.compat.MenuItemCompat;
import com.adblog.control.Util;
import com.adblog.util.SessionList;
import com.adblog.util.TermSettings;
import com.tool.androidesk.R;
/**
* A terminal emulator activity.
*/
public class Term extends Activity implements UpdateCallback {
/**
* 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 SharedPreferences mPrefs;
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 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 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 = 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();
}
}
};
// 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;
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) {
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) {
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;
}
}
}
private View.OnKeyListener mBackKeyListener = new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK
&& mActionBarMode == TermSettings.ACTION_BAR_MODE_HIDES
&& 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.e(TermDebug.LOG_TAG, "onCreate");
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
mSettings = new TermSettings(getResources(), mPrefs);
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);
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 (!bindService(TSIntent, mTSConnection, BIND_AUTO_CREATE)) {
Log.w(TermDebug.LOG_TAG, "bind to service failed!");
}
if (AndroidCompat.SDK >= 11) {
int actionBarMode = mSettings.actionBarMode();
mActionBarMode = actionBarMode;
switch (actionBarMode) {
case TermSettings.ACTION_BAR_MODE_ALWAYS_VISIBLE:
setTheme(R.style.Theme_Holo);
break;
case TermSettings.ACTION_BAR_MODE_HIDES:
setTheme(R.style.Theme_Holo_ActionBarOverlay);
break;
}
}
setContentView(R.layout.term_activity);
mViewFlipper = (TermViewFlipper) findViewById(VIEW_FLIPPER);
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());
updatePrefs();
mAlreadyStarted = true;
}
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);
}
private void populateViewFlipper() {
if (mTermService != null) {
mTermSessions = mTermService.getSessions();
mTermSessions.addCallback(this);
if (mTermSessions.size() == 0) {
mTermSessions.add(createTermSession());
}
for (TermSession session : mTermSessions) {
EmulatorView view = createEmulatorView(session);
mViewFlipper.addView(view);
}
updatePrefs();
Intent intent = getIntent();
int flags = intent.getFlags();
String action = intent.getAction();
if ((flags & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0
&& action != null) {
if (action.equals(RemoteInterface.PRIVACT_OPEN_NEW_WINDOW)) {
mViewFlipper.setDisplayedChild(mTermSessions.size() - 1);
} else if (action.equals(RemoteInterface.PRIVACT_SWITCH_WINDOW)) {
int target = intent.getIntExtra(
RemoteInterface.PRIVEXTRA_TARGET_WINDOW, -1);
if (target >= 0) {
mViewFlipper.setDisplayedChild(target);
}
}
}
mViewFlipper.resumeCurrentView();
}
}
private void populateWindowList() {
if (mActionBar == null) {
// Not needed
return;
}
if (mTermSessions != null) {
int position = mViewFlipper.getDisplayedChild();
WindowListAdapter adapter = mWinListAdapter;
if (adapter == null) {
adapter = new WindowListActionBarAdapter(mTermSessions);
mWinListAdapter = adapter;
SessionList sessions = mTermSessions;
sessions.addCallback(adapter);
sessions.addTitleChangedListener(adapter);
mViewFlipper.addCallback(adapter);
mActionBar.setListNavigationCallbacks(adapter,
mWinListItemSelected);
} else {
adapter.setSessions(mTermSessions);
}
mActionBar.setSelectedNavigationItem(position);
}
}
@Override
public void onDestroy() {
super.onDestroy();
mViewFlipper.removeAllViews();
unbindService(mTSConnection);
if (mStopServiceOnFinish) {
stopService(TSIntent);
}
mTermService = null;
mTSConnection = null;
if (mWakeLock.isHeld()) {
mWakeLock.release();
}
if (mWifiLock.isHeld()) {
mWifiLock.release();
}
}
private void restart() {
startActivity(getIntent());
finish();
}
protected static TermSession createTermSession(Context context,
TermSettings settings, String initialCommand) {
ShellTermSession session = new ShellTermSession(settings,
initialCommand);
// XXX We should really be able to fetch this from within TermSession
session.setProcessExitMessage(context
.getString(R.string.process_exit_message));
return session;
}
private TermSession createTermSession() {
TermSettings settings = mSettings;
TermSession session = createTermSession(this, settings,
settings.getInitialCommand());
session.setFinishCallback(mTermService);
return session;
}
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(mBackKeyListener);
registerForContextMenu(emulatorView);
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() {
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
ColorScheme colorScheme = new ColorScheme(mSettings.getColorScheme());
mViewFlipper.updatePrefs(mSettings);
for (View v : mViewFlipper) {
((EmulatorView) v).setDensity(metrics);
((TermView) v).updatePrefs(mSettings);
}
if (mTermSessions != null) {
for (TermSession session : mTermSessions) {
((ShellTermSession) 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) {
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);
}
@Override
public void onResume() {
super.onResume();
SessionList sessions = mTermSessions;
TermViewFlipper viewFlipper = mViewFlipper;
if (sessions != null) {
sessions.addCallback(this);
WindowListAdapter adapter = mWinListAdapter;
if (adapter != null) {
sessions.addCallback(adapter);
sessions.addTitleChangedListener(adapter);
viewFlipper.addCallback(adapter);
}
}
if (sessions != null && sessions.size() < viewFlipper.getChildCount()) {
for (int i = 0; i < viewFlipper.getChildCount(); ++i) {
EmulatorView v = (EmulatorView) viewFlipper.getChildAt(i);
if (!sessions.contains(v.getTermSession())) {
v.onPause();
viewFlipper.removeView(v);
--i;
}
}
}
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
// the HOME dir needs to be set here since it comes from Context
SharedPreferences.Editor editor = mPrefs.edit();
String defValue = getDir("HOME", MODE_PRIVATE).getAbsolutePath();
String homePath = mPrefs.getString("home_path", defValue);
editor.putString("home_path", homePath);
editor.commit();
mSettings.readPrefs(mPrefs);
updatePrefs();
if (onResumeSelectWindow >= 0) {
viewFlipper.setDisplayedChild(onResumeSelectWindow);
onResumeSelectWindow = -1;
}
viewFlipper.onResume();
}
@Override
public void onPause() {
super.onPause();
SessionList sessions = mTermSessions;
TermViewFlipper viewFlipper = mViewFlipper;
viewFlipper.onPause();
if (sessions != null) {
sessions.removeCallback(this);
WindowListAdapter adapter = mWinListAdapter;
if (adapter != null) {
sessions.removeCallback(adapter);
sessions.removeTitleChangedListener(adapter);
viewFlipper.removeCallback(adapter);
}
}
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 = viewFlipper.getWindowToken();
new Thread() {
@Override
public void run() {
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(token, 0);
}
}.start();
}
private boolean checkHaveFullHwKeyboard(Configuration c) {
return (c.keyboard == Configuration.KEYBOARD_QWERTY)
&& (c.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mHaveFullHwKeyboard = checkHaveFullHwKeyboard(newConfig);
EmulatorView v = (EmulatorView) mViewFlipper.getCurrentView();
if (v != null) {
v.updateSize(false);
}
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);
MenuItemCompat.setShowAsAction(menu.findItem(R.id.menu_new_window),
MenuItemCompat.SHOW_AS_ACTION_ALWAYS);
MenuItemCompat.setShowAsAction(menu.findItem(R.id.menu_close_window),
MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.menu_preferences) {
doPreferences();
} else if (id == R.id.menu_new_window) {
doCreateNewWindow();
} else if (id == R.id.menu_close_window) {
confirmCloseWindow();
} else if (id == R.id.menu_window_list) {
startActivityForResult(new Intent(this, WindowList.class),
REQUEST_CHOOSE_WINDOW);
} else if (id == 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();
} else if (id == R.id.menu_send_email) {
doEmailTranscript();
} else if (id == R.id.menu_special_keys) {
doDocumentKeys();
} else if (id == R.id.menu_toggle_soft_keyboard) {
doToggleSoftKeyboard();
}
// else if (id == R.id.menu_toggle_wakelock) {
// doToggleWakeLock();
// } else if (id == R.id.menu_toggle_wifilock) {
// doToggleWifiLock();
// }
// Hide the action bar if appropriate
if (mActionBarMode == TermSettings.ACTION_BAR_MODE_HIDES) {
mActionBar.hide();
}
return super.onOptionsItemSelected(item);
}
private void doCreateNewWindow() {
if (mTermSessions == null) {
Log.w(TermDebug.LOG_TAG,
"Couldn't create new window because mTermSessions == null");
return;
}
TermSession session = createTermSession();
mTermSessions.add(session);
TermView view = createEmulatorView(session);
view.updatePrefs(mSettings);
mViewFlipper.addView(view);
mViewFlipper.setDisplayedChild(mViewFlipper.getChildCount() - 1);
}
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;
}
TermSession session = mTermSessions.remove(mViewFlipper
.getDisplayedChild());
view.onPause();
session.finish();
mViewFlipper.removeView(view);
if (mTermSessions.size() == 0) {
mStopServiceOnFinish = true;
finish();
} else {
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
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 (action == null) {
return;
}
if (action.equals(RemoteInterface.PRIVACT_OPEN_NEW_WINDOW)) {
// New session was created, add an EmulatorView to match
SessionList sessions = mTermSessions;
if (sessions == null) {
// Presumably populateViewFlipper() will do this later ...
return;
}
int position = sessions.size() - 1;
TermSession session = sessions.get(position);
EmulatorView view = createEmulatorView(session);
mViewFlipper.addView(view);
onResumeSelectWindow = position;
} else if (action.equals(RemoteInterface.PRIVACT_SWITCH_WINDOW)) {
int target = intent.getIntExtra(
RemoteInterface.PRIVEXTRA_TARGET_WINDOW, -1);
if (target >= 0) {
onResumeSelectWindow = target;
}
}
}
@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);
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;
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 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.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;
default:
return false;
}
case KeyEvent.KEYCODE_MENU:
if (mActionBar != null && !mActionBar.isShowing()) {
mActionBar.show();
return true;
} else {
return super.onKeyUp(keyCode, event);
}
default:
return super.onKeyUp(keyCode, event);
}
}
// 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 = "logcatlog@gmail.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+" "+Util.getTitle());
intent.putExtra(Intent.EXTRA_TEXT,
Util.getDevicesInfo(getBaseContext()) + "\n"
+ 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(getCurrentTermSession().getTranscriptText().trim());
}
private void doPaste() {
ClipboardManagerCompat clip = ClipboardManagerCompatFactory
.getManager(getApplicationContext());
CharSequence paste = clip.getText();
byte[] utf8;
try {
utf8 = paste.toString().getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
Log.e(TermDebug.LOG_TAG, "UTF-8 encoding not found.");
return;
}
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 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_ALWAYS_VISIBLE:
if (!mHaveFullHwKeyboard) {
doToggleSoftKeyboard();
}
break;
case TermSettings.ACTION_BAR_MODE_HIDES:
if (mHaveFullHwKeyboard || y < height / 2) {
doToggleActionBar();
return;
} else {
doToggleSoftKeyboard();
}
break;
}
getCurrentEmulatorView().requestFocus();
}
}