package jackpal.androidterm.sample.telnet;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.Editable;
import android.text.method.TextKeyListener;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import jackpal.androidterm.emulatorview.EmulatorView;
import jackpal.androidterm.emulatorview.TermSession;
/**
* This sample activity demonstrates the use of EmulatorView.
*
* This activity also demonstrates how to set up a simple TermSession connected
* to a local program. The Telnet connection demonstrates a more complex case;
* see the TelnetSession class for more details.
*/
public class TermActivity extends Activity
{
final private static String TAG = "TermActivity";
private EditText mEntry;
private EmulatorView mEmulatorView;
private TermSession mSession;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.term_activity);
/* Text entry box at the bottom of the activity. Note that you can
also send input (whether from a hardware device or soft keyboard)
directly to the EmulatorView. */
mEntry = (EditText) findViewById(R.id.term_entry);
mEntry.setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int action, KeyEvent ev) {
// Ignore enter-key-up events
if (ev != null && ev.getAction() == KeyEvent.ACTION_UP) {
return false;
}
// Don't try to send something if we're not connected yet
TermSession session = mSession;
if (mSession == null) {
return true;
}
Editable e = (Editable) v.getText();
// Write to the terminal session
session.write(e.toString());
session.write('\r');
TextKeyListener.clear(e);
return true;
}
});
/* Sends the content of the text entry box to the terminal, without
sending a carriage return afterwards */
Button sendButton = (Button) findViewById(R.id.term_entry_send);
sendButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Don't try to send something if we're not connected yet
TermSession session = mSession;
if (mSession == null) {
return;
}
Editable e = (Editable) mEntry.getText();
session.write(e.toString());
TextKeyListener.clear(e);
}
});
/**
* EmulatorView setup.
*/
EmulatorView view = (EmulatorView) findViewById(R.id.emulatorView);
mEmulatorView = view;
/* Let the EmulatorView know the screen's density. */
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
view.setDensity(metrics);
/* Create a TermSession. */
Intent myIntent = getIntent();
String sessionType = myIntent.getStringExtra("type");
TermSession session;
if (sessionType != null && sessionType.equals("telnet")) {
/* Telnet connection: we need to do the network connect on a
separate thread, so kick that off and wait for it to finish. */
connectToTelnet(myIntent.getStringExtra("host"));
return;
} else {
// Create a local shell session.
session = createLocalTermSession();
if (session == null) {
finish();
return;
}
mSession = session;
}
/* Attach the TermSession to the EmulatorView. */
view.attachSession(session);
/* That's all you have to do! The EmulatorView will call the attached
TermSession's initializeEmulator() automatically, once it can
calculate the appropriate screen size for the terminal emulator. */
}
@Override
protected void onResume() {
super.onResume();
/* You should call this to let EmulatorView know that it's visible
on screen. */
mEmulatorView.onResume();
mEntry.requestFocus();
}
@Override
protected void onPause() {
/* You should call this to let EmulatorView know that it's no longer
visible on screen. */
mEmulatorView.onPause();
super.onPause();
}
@Override
protected void onDestroy() {
/**
* Finish the TermSession when we're destroyed. This will free
* resources, stop I/O threads, and close the I/O streams attached
* to the session.
*
* For the local session, closing the streams will kill the shell; for
* the Telnet session, it closes the network connection.
*/
if (mSession != null) {
mSession.finish();
}
super.onDestroy();
}
/**
* Create a TermSession connected to a local shell.
*/
private TermSession createLocalTermSession() {
/* Instantiate the TermSession ... */
TermSession session = new TermSession();
/* ... create a process ... */
/* TODO:Make local session work without execpty.
String execPath = LaunchActivity.getDataDir(this) + "/bin/execpty";
ProcessBuilder execBuild =
new ProcessBuilder(execPath, "/system/bin/sh", "-");
*/
ProcessBuilder execBuild =
new ProcessBuilder("/system/bin/sh", "-");
execBuild.redirectErrorStream(true);
Process exec = null;
try {
exec = execBuild.start();
} catch (Exception e) {
Log.e(TAG, "Could not start terminal process.", e);
return null;
}
/* ... and connect the process's I/O streams to the TermSession. */
session.setTermIn(exec.getInputStream());
session.setTermOut(exec.getOutputStream());
/* You're done! */
return session;
/**
* NB: You can invoke a program without using execpty or a native code
* method, but the results may not be what you expect, because the
* process will be connected to a pipe, not a tty device. tty devices
* provide services such as flow control and input/output translation
* which many programs expect.
*
* If you do connect a program directly to a TermSession without using
* a tty, you should probably at a minimum translate '\r' (sent by the
* Enter key) to '\n' (which most programs expect as their newline
* input) in write(), and translate '\n' (sent by most programs to
* indicate a newline) to '\r\n' (which the terminal emulator needs to
* actually start a new line without overdrawing text or "staircase
* effect") in processInput(), before sending it to the terminal
* emulator.
*
* For an example of how to obtain and use a tty device in native code,
* see assets-src/execpty.c.
*/
}
/**
* Connect to the Telnet server.
*/
public void connectToTelnet(String server) {
String[] telnetServer = server.split(":", 2);
final String hostname = telnetServer[0];
int port = 23;
if (telnetServer.length == 2) {
port = Integer.parseInt(telnetServer[1]);
}
final int portNum = port;
/* On Android API >= 11 (Honeycomb and later), network operations
must be done off the main thread, so kick off a new thread to
perform the connect. */
new Thread() {
public void run() {
// Connect to the server
try {
Socket socket = new Socket(hostname, portNum);
mSocket = socket;
} catch (IOException e) {
Log.e(TAG, "Could not create socket", e);
return;
}
// Notify the main thread of the connection
mHandler.sendEmptyMessage(MSG_CONNECTED);
}
}.start();
}
/**
* Handler which will receive the message from the Telnet connect thread
* that the connection has been established.
*/
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_CONNECTED) {
createTelnetSession();
}
}
};
Socket mSocket;
private static final int MSG_CONNECTED = 1;
/* Create the TermSession which will handle the Telnet protocol and
terminal emulation. */
private void createTelnetSession() {
Socket socket = mSocket;
// Get the socket's input and output streams
InputStream termIn;
OutputStream termOut;
try {
termIn = socket.getInputStream();
termOut = socket.getOutputStream();
} catch (IOException e) {
// Handle exception here
return;
}
/* Create the TermSession and attach it to the view. See the
TelnetSession class for details. */
TermSession session = new TelnetSession(termIn, termOut);
mEmulatorView.attachSession(session);
mSession = session;
}
}