1ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey/*
2ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey * Copyright (C) 2013 The Android Open Source Project
3ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey *
4ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
5ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey * you may not use this file except in compliance with the License.
6ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey * You may obtain a copy of the License at
7ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey *
8ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
9ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey *
10ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey * Unless required by applicable law or agreed to in writing, software
11ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
12ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey * See the License for the specific language governing permissions and
14ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey * limitations under the License.
15ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey */
16ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey
17ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkeypackage com.android.terminal;
18ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey
1900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkeyimport static com.android.terminal.Terminal.TAG;
2000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey
21ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkeyimport android.app.Activity;
22de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkeyimport android.content.ComponentName;
23de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkeyimport android.content.Context;
24de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkeyimport android.content.Intent;
25de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkeyimport android.content.ServiceConnection;
26ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkeyimport android.os.Bundle;
27de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkeyimport android.os.IBinder;
2800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkeyimport android.os.Parcelable;
29de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkeyimport android.support.v4.view.PagerAdapter;
30de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkeyimport android.support.v4.view.PagerTitleStrip;
31de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkeyimport android.support.v4.view.ViewPager;
32ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkeyimport android.util.Log;
3300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkeyimport android.util.SparseArray;
34de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkeyimport android.view.Menu;
35de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkeyimport android.view.MenuItem;
36de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkeyimport android.view.View;
37de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkeyimport android.view.ViewGroup;
38ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey
39de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey/**
40de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey * Activity that displays all {@link Terminal} instances running in a bound
41de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey * {@link TerminalService}.
42de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey */
43ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkeypublic class TerminalActivity extends Activity {
44ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey
45de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey    private TerminalService mService;
46de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey
47de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey    private ViewPager mPager;
48de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey    private PagerTitleStrip mTitles;
49de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey
50de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey    private final ServiceConnection mServiceConn = new ServiceConnection() {
51de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        @Override
52de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        public void onServiceConnected(ComponentName name, IBinder service) {
53de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            mService = ((TerminalService.ServiceBinder) service).getService();
54de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey
55de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            final int size = mService.getTerminals().size();
56de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            Log.d(TAG, "Bound to service with " + size + " active terminals");
57de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey
58de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            // Give ourselves at least one terminal session
59de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            if (size == 0) {
60de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey                mService.createTerminal();
61de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            }
62de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey
63de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            // Bind UI to known terminals
64de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            mTermAdapter.notifyDataSetChanged();
6500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            invalidateOptionsMenu();
66de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        }
67de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey
68de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        @Override
69de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        public void onServiceDisconnected(ComponentName name) {
70de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            mService = null;
71de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            throw new RuntimeException("Service in same process disconnected?");
72de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        }
73de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey    };
74de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey
75de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey    private final PagerAdapter mTermAdapter = new PagerAdapter() {
7600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        private SparseArray<SparseArray<Parcelable>>
7700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey                mSavedState = new SparseArray<SparseArray<Parcelable>>();
7800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey
79de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        @Override
80de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        public int getCount() {
81de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            if (mService != null) {
82de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey                return mService.getTerminals().size();
83de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            } else {
84de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey                return 0;
85de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            }
86de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        }
87de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey
88de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        @Override
89de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        public Object instantiateItem(ViewGroup container, int position) {
90de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            final TerminalView view = new TerminalView(container.getContext());
9100b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            view.setId(android.R.id.list);
9200b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey
9300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            final Terminal term = mService.getTerminals().valueAt(position);
94de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            view.setTerminal(term);
9500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey
9600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            final SparseArray<Parcelable> state = mSavedState.get(term.key);
9700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            if (state != null) {
9800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey                view.restoreHierarchyState(state);
9900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            }
10000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey
101de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            container.addView(view);
1028dc54499bf4afccb60cd3500b03767cab9e2ab43Tom Marshall            view.requestFocus();
103de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            return view;
104de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        }
105de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey
106de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        @Override
107de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        public void destroyItem(ViewGroup container, int position, Object object) {
108de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            final TerminalView view = (TerminalView) object;
10900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey
11000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            final int key = view.getTerminal().key;
11100b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            SparseArray<Parcelable> state = mSavedState.get(key);
11200b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            if (state == null) {
11300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey                state = new SparseArray<Parcelable>();
11400b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey                mSavedState.put(key, state);
11500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            }
11600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            view.saveHierarchyState(state);
11700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey
118de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            view.setTerminal(null);
119de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            container.removeView(view);
120de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        }
121de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey
122de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        @Override
123de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        public int getItemPosition(Object object) {
12400b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            final TerminalView view = (TerminalView) object;
12500b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            final int key = view.getTerminal().key;
12600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            final int index = mService.getTerminals().indexOfKey(key);
127de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            if (index == -1) {
128de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey                return POSITION_NONE;
129de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            } else {
130de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey                return index;
131de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            }
132de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        }
133de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey
134de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        @Override
135de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        public boolean isViewFromObject(View view, Object object) {
136de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            return view == object;
137de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        }
138de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey
139de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        @Override
140de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        public CharSequence getPageTitle(int position) {
14100b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey            return mService.getTerminals().valueAt(position).getTitle();
142de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        }
143de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey    };
144de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey
145ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey    @Override
146ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey    protected void onCreate(Bundle savedInstanceState) {
147ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey        super.onCreate(savedInstanceState);
148ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey
149de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        setContentView(R.layout.activity);
150de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey
151de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        mPager = (ViewPager) findViewById(R.id.pager);
152de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        mTitles = (PagerTitleStrip) findViewById(R.id.titles);
153de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey
154de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        mPager.setAdapter(mTermAdapter);
155de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey    }
156de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey
157de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey    @Override
158de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey    protected void onStart() {
159de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        super.onStart();
160de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        bindService(
161de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey                new Intent(this, TerminalService.class), mServiceConn, Context.BIND_AUTO_CREATE);
162de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey    }
163479bd643981271fb0edf756ae5915e44a7352c4dJeff Sharkey
164de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey    @Override
165de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey    protected void onStop() {
166de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        super.onStop();
167de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        unbindService(mServiceConn);
168de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey    }
169410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
170de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey    @Override
171de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey    public boolean onCreateOptionsMenu(Menu menu) {
172de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        getMenuInflater().inflate(R.menu.activity, menu);
173de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        return true;
174de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey    }
175410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey
176de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey    @Override
17700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    public boolean onPrepareOptionsMenu(Menu menu) {
17800b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        super.onPrepareOptionsMenu(menu);
17900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        menu.findItem(R.id.menu_close_tab).setEnabled(mTermAdapter.getCount() > 0);
18000b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey        return true;
18100b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    }
18200b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey
18300b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey    @Override
184de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey    public boolean onOptionsItemSelected(MenuItem item) {
185de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        switch (item.getItemId()) {
186de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            case R.id.menu_new_tab: {
187de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey                mService.createTerminal();
188de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey                mTermAdapter.notifyDataSetChanged();
18900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey                invalidateOptionsMenu();
190de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey                final int index = mService.getTerminals().size() - 1;
191de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey                mPager.setCurrentItem(index, true);
192de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey                return true;
193de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            }
194de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            case R.id.menu_close_tab: {
195de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey                final int index = mPager.getCurrentItem();
19600b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey                final int key = mService.getTerminals().keyAt(index);
19700b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey                mService.destroyTerminal(key);
198de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey                mTermAdapter.notifyDataSetChanged();
19900b00812cd0c883c2380065d7fda29512d5477f0Jeff Sharkey                invalidateOptionsMenu();
200de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey                return true;
201de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey            }
202de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        }
203de15e79aadde33fd8c880c19bd4fc6caca0bf795Jeff Sharkey        return false;
204ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey    }
205ef946f3ae80556ab221265c0bf1c560683ea27f6Jeff Sharkey}
206