diff options
Diffstat (limited to 'src/com/android')
-rw-r--r-- | src/com/android/terminal/Terminal.java | 17 | ||||
-rw-r--r-- | src/com/android/terminal/TerminalActivity.java | 141 | ||||
-rw-r--r-- | src/com/android/terminal/TerminalService.java | 73 | ||||
-rw-r--r-- | src/com/android/terminal/TerminalView.java | 41 |
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"); } } |