summaryrefslogtreecommitdiffstats
path: root/src/com/android
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android')
-rw-r--r--src/com/android/terminal/Terminal.java17
-rw-r--r--src/com/android/terminal/TerminalActivity.java141
-rw-r--r--src/com/android/terminal/TerminalService.java73
-rw-r--r--src/com/android/terminal/TerminalView.java41
4 files changed, 258 insertions, 14 deletions
diff --git a/src/com/android/terminal/Terminal.java b/src/com/android/terminal/Terminal.java
index d37e6a8..c9e45e7 100644
--- a/src/com/android/terminal/Terminal.java
+++ b/src/com/android/terminal/Terminal.java
@@ -24,6 +24,8 @@ import android.graphics.Color;
public class Terminal {
private static final String TAG = "Terminal";
+ private static int sNumber = 0;
+
static {
System.loadLibrary("jni_terminal");
}
@@ -58,6 +60,8 @@ public class Terminal {
private final int mNativePtr;
private final Thread mThread;
+ private String mTitle;
+
private TerminalClient mClient;
private final TerminalCallbacks mCallbacks = new TerminalCallbacks() {
@@ -90,6 +94,7 @@ public class Terminal {
public Terminal() {
mNativePtr = nativeInit(mCallbacks, 25, 80);
+ mTitle = TAG + " " + sNumber++;
mThread = new Thread(TAG) {
@Override
public void run() {
@@ -105,6 +110,12 @@ public class Terminal {
mThread.start();
}
+ public void stop() {
+ if (nativeStop(mNativePtr) != 0) {
+ throw new IllegalStateException("stop failed");
+ }
+ }
+
public void setClient(TerminalClient client) {
mClient = client;
}
@@ -135,8 +146,14 @@ public class Terminal {
}
}
+ public String getTitle() {
+ // TODO: hook up to title passed through termprop
+ return mTitle;
+ }
+
private static native int nativeInit(TerminalCallbacks callbacks, int rows, int cols);
private static native int nativeRun(int ptr);
+ private static native int nativeStop(int ptr);
private static native int nativeFlushDamage(int ptr);
private static native int nativeResize(int ptr, int rows, int cols);
diff --git a/src/com/android/terminal/TerminalActivity.java b/src/com/android/terminal/TerminalActivity.java
index bef1859..99d2be6 100644
--- a/src/com/android/terminal/TerminalActivity.java
+++ b/src/com/android/terminal/TerminalActivity.java
@@ -17,23 +17,154 @@
package com.android.terminal;
import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
import android.os.Bundle;
+import android.os.IBinder;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.PagerTitleStrip;
+import android.support.v4.view.ViewPager;
import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+/**
+ * Activity that displays all {@link Terminal} instances running in a bound
+ * {@link TerminalService}.
+ */
public class TerminalActivity extends Activity {
private static final String TAG = "Terminal";
+ private TerminalService mService;
+
+ private ViewPager mPager;
+ private PagerTitleStrip mTitles;
+
+ private final ServiceConnection mServiceConn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mService = ((TerminalService.ServiceBinder) service).getService();
+
+ final int size = mService.getTerminals().size();
+ Log.d(TAG, "Bound to service with " + size + " active terminals");
+
+ // Give ourselves at least one terminal session
+ if (size == 0) {
+ mService.createTerminal();
+ }
+
+ // Bind UI to known terminals
+ mTermAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mService = null;
+ throw new RuntimeException("Service in same process disconnected?");
+ }
+ };
+
+ private final PagerAdapter mTermAdapter = new PagerAdapter() {
+ @Override
+ public int getCount() {
+ if (mService != null) {
+ return mService.getTerminals().size();
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public Object instantiateItem(ViewGroup container, int position) {
+ final Terminal term = mService.getTerminals().get(position);
+ final TerminalView view = new TerminalView(container.getContext());
+ view.setTerminal(term);
+ container.addView(view);
+ return view;
+ }
+
+ @Override
+ public void destroyItem(ViewGroup container, int position, Object object) {
+ final TerminalView view = (TerminalView) object;
+ view.setTerminal(null);
+ container.removeView(view);
+ }
+
+ @Override
+ public int getItemPosition(Object object) {
+ final int index = mService.getTerminals().indexOf(object);
+ if (index == -1) {
+ return POSITION_NONE;
+ } else {
+ return index;
+ }
+ }
+
+ @Override
+ public boolean isViewFromObject(View view, Object object) {
+ return view == object;
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ return mService.getTerminals().get(position).getTitle();
+ }
+ };
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- final Terminal term = new Terminal();
- term.start();
+ setContentView(R.layout.activity);
+
+ mPager = (ViewPager) findViewById(R.id.pager);
+ mTitles = (PagerTitleStrip) findViewById(R.id.titles);
+
+ mPager.setAdapter(mTermAdapter);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ bindService(
+ new Intent(this, TerminalService.class), mServiceConn, Context.BIND_AUTO_CREATE);
+ }
- final TerminalView view = new TerminalView(this, term);
+ @Override
+ protected void onStop() {
+ super.onStop();
+ unbindService(mServiceConn);
+ }
- setContentView(view);
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.activity, menu);
+ return true;
+ }
- Log.d(TAG, "Rows: " + term.getRows());
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_new_tab: {
+ mService.createTerminal();
+ mTermAdapter.notifyDataSetChanged();
+ final int index = mService.getTerminals().size() - 1;
+ mPager.setCurrentItem(index, true);
+ return true;
+ }
+ case R.id.menu_close_tab: {
+ final int index = mPager.getCurrentItem();
+ final Terminal term = mService.getTerminals().get(index);
+ mService.destroyTerminal(term);
+ // TODO: ask adamp about buggy zero item behavior
+ mTermAdapter.notifyDataSetChanged();
+ return true;
+ }
+ }
+ return false;
}
}
diff --git a/src/com/android/terminal/TerminalService.java b/src/com/android/terminal/TerminalService.java
new file mode 100644
index 0000000..ebbdcce
--- /dev/null
+++ b/src/com/android/terminal/TerminalService.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 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.android.terminal;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Background service that keeps {@link Terminal} instances running and warm
+ * when UI isn't present.
+ */
+public class TerminalService extends Service {
+ private static final String TAG = "Terminal";
+
+ private final ArrayList<Terminal> mTerminals = new ArrayList<Terminal>();
+
+ public class ServiceBinder extends Binder {
+ public TerminalService getService() {
+ return TerminalService.this;
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new ServiceBinder();
+ }
+
+ public List<Terminal> getTerminals() {
+ return Collections.unmodifiableList(mTerminals);
+ }
+
+ public Terminal createTerminal() {
+ // If our first terminal, start ourselves as long-lived service
+ if (mTerminals.isEmpty()) {
+ startService(new Intent(this, TerminalService.class));
+ }
+
+ final Terminal term = new Terminal();
+ term.start();
+ mTerminals.add(term);
+ return term;
+ }
+
+ public void destroyTerminal(Terminal term) {
+ term.stop();
+ mTerminals.remove(term);
+
+ // If our last terminal, tear down long-lived service
+ if (mTerminals.isEmpty()) {
+ stopService(new Intent(this, TerminalService.class));
+ }
+ }
+}
diff --git a/src/com/android/terminal/TerminalView.java b/src/com/android/terminal/TerminalView.java
index 35192e9..64aeabd 100644
--- a/src/com/android/terminal/TerminalView.java
+++ b/src/com/android/terminal/TerminalView.java
@@ -35,11 +35,11 @@ import com.android.terminal.Terminal.TerminalClient;
*/
public class TerminalView extends View {
private static final String TAG = "Terminal";
+ private static final boolean LOGD = true;
private static final int MAX_RUN_LENGTH = 128;
private final Context mContext;
- private final Terminal mTerm;
private final Paint mBgPaint = new Paint();
private final Paint mTextPaint = new Paint();
@@ -49,6 +49,8 @@ public class TerminalView extends View {
/** Screen coordinates to draw chars into */
private final float[] mPos;
+ private Terminal mTerm;
+
private int mCharTop;
private int mCharWidth;
private int mCharHeight;
@@ -59,7 +61,7 @@ public class TerminalView extends View {
private TerminalClient mClient = new TerminalClient() {
@Override
public void damage(int startRow, int endRow, int startCol, int endCol) {
- Log.d(TAG, "damage(" + startRow + ", " + endRow + ", " + startCol + ", " + endCol + ")");
+ if (LOGD) Log.d(TAG, "damage(" + startRow + ", " + endRow + ", " + startCol + ", " + endCol + ")");
// Invalidate region on screen
final int top = startRow * mCharHeight;
@@ -86,10 +88,9 @@ public class TerminalView extends View {
}
};
- public TerminalView(Context context, Terminal term) {
+ public TerminalView(Context context) {
super(context);
mContext = context;
- mTerm = term;
mRun = new Terminal.CellRun();
mRun.data = new char[MAX_RUN_LENGTH];
@@ -110,20 +111,37 @@ public class TerminalView extends View {
});
}
+ public void setTerminal(Terminal term) {
+ final Terminal orig = mTerm;
+ if (orig != null) {
+ orig.setClient(null);
+ }
+ mTerm = term;
+ if (term != null) {
+ term.setClient(mClient);
+ }
+ updateTerminalSize();
+ }
+
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mTerm.setClient(mClient);
+ if (mTerm != null) {
+ mTerm.setClient(mClient);
+ }
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- mTerm.setClient(null);
+ if (mTerm != null) {
+ mTerm.setClient(null);
+ }
}
public void setTextSize(float textSize) {
mTextPaint.setTypeface(Typeface.MONOSPACE);
+ mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(textSize);
// Read metrics to get exact pixel dimensions
@@ -149,11 +167,10 @@ public class TerminalView extends View {
* and request that {@link Terminal} change to that size.
*/
public void updateTerminalSize() {
- if (getWidth() > 0 && getHeight() > 0) {
+ if (getWidth() > 0 && getHeight() > 0 && mTerm != null) {
final int rows = getHeight() / mCharHeight;
final int cols = getWidth() / mCharWidth;
mTerm.resize(rows, cols);
- mTerm.flushDamage();
}
}
@@ -169,6 +186,12 @@ public class TerminalView extends View {
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
+ if (mTerm == null) {
+ Log.w(TAG, "onDraw() without a terminal");
+ canvas.drawColor(Color.MAGENTA);
+ return;
+ }
+
final long start = SystemClock.elapsedRealtime();
// Only draw dirty region of console
@@ -210,6 +233,6 @@ public class TerminalView extends View {
}
final long delta = SystemClock.elapsedRealtime() - start;
- Log.d(TAG, "onDraw() took " + delta + "ms");
+ if (LOGD) Log.d(TAG, "onDraw() took " + delta + "ms");
}
}