Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
[fw/altos] / altosdroid / src / org / altusmetrum / AltosDroid / AltosDroid.java
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.altusmetrum.AltosDroid;
18
19 import android.app.Activity;
20 import android.bluetooth.BluetoothAdapter;
21 import android.bluetooth.BluetoothDevice;
22 import android.content.Intent;
23 import android.os.Bundle;
24 import android.os.Handler;
25 import android.os.Message;
26 import android.text.method.ScrollingMovementMethod;
27 import android.util.Log;
28 import android.view.KeyEvent;
29 import android.view.Menu;
30 import android.view.MenuInflater;
31 import android.view.MenuItem;
32 import android.view.View;
33 import android.view.Window;
34 import android.view.View.OnClickListener;
35 import android.view.inputmethod.EditorInfo;
36 import android.widget.Button;
37 import android.widget.EditText;
38 import android.widget.TextView;
39 import android.widget.Toast;
40 import org.altusmetrum.AltosDroid.R;
41 import org.altusmetrum.AltosLib.*;
42
43 /**
44  * This is the main Activity that displays the current chat session.
45  */
46 public class AltosDroid extends Activity {
47     // Debugging
48     private static final String TAG = "AltosDroid";
49     private static final boolean D = true;
50
51     // Message types sent from the BluetoothChatService Handler
52     public static final int MESSAGE_STATE_CHANGE = 1;
53     public static final int MESSAGE_READ = 2;
54     public static final int MESSAGE_WRITE = 3;
55     public static final int MESSAGE_DEVICE_NAME = 4;
56     public static final int MESSAGE_TOAST = 5;
57
58     // Key names received from the BluetoothChatService Handler
59     public static final String DEVICE_NAME = "device_name";
60     public static final String TOAST = "toast";
61
62     // Intent request codes
63     private static final int REQUEST_CONNECT_DEVICE = 1;
64     private static final int REQUEST_ENABLE_BT      = 2;
65
66     // Layout Views
67     private TextView mTitle;
68     private TextView mSerialView;
69     private EditText mOutEditText;
70     private Button mSendButton;
71
72     // Name of the connected device
73     private String mConnectedDeviceName = null;
74     // String buffer for outgoing messages
75     private StringBuffer mOutStringBuffer;
76     // Local Bluetooth adapter
77     private BluetoothAdapter mBluetoothAdapter = null;
78     // Member object for the chat services
79     private BluetoothChatService mChatService = null;
80
81
82     @Override
83     public void onCreate(Bundle savedInstanceState) {
84         super.onCreate(savedInstanceState);
85         if(D) Log.e(TAG, "+++ ON CREATE +++");
86
87         // Set up the window layout
88         requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
89         setContentView(R.layout.main);
90         getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);
91
92         // Set up the custom title
93         mTitle = (TextView) findViewById(R.id.title_left_text);
94         mTitle.setText(R.string.app_name);
95         mTitle = (TextView) findViewById(R.id.title_right_text);
96
97         // Get local Bluetooth adapter
98         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
99
100         // If the adapter is null, then Bluetooth is not supported
101         if (mBluetoothAdapter == null) {
102             Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
103             finish();
104             return;
105         }
106     }
107
108     @Override
109     public void onStart() {
110         super.onStart();
111         if(D) Log.e(TAG, "++ ON START ++");
112
113         // If BT is not on, request that it be enabled.
114         // setupChat() will then be called during onActivityResult
115         if (!mBluetoothAdapter.isEnabled()) {
116             Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
117             startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
118         // Otherwise, setup the chat session
119         } else {
120             if (mChatService == null) setupChat();
121         }
122     }
123
124     @Override
125     public synchronized void onResume() {
126         super.onResume();
127         if(D) Log.e(TAG, "+ ON RESUME +");
128
129         // Performing this check in onResume() covers the case in which BT was
130         // not enabled during onStart(), so we were paused to enable it...
131         // onResume() will be called when ACTION_REQUEST_ENABLE activity returns.
132         if (mChatService != null) {
133             // Only if the state is STATE_NONE, do we know that we haven't started already
134             if (mChatService.getState() == BluetoothChatService.STATE_NONE) {
135               // Start the Bluetooth chat services
136               mChatService.start();
137             }
138         }
139     }
140
141     @Override
142     public synchronized void onPause() {
143         super.onPause();
144         if(D) Log.e(TAG, "- ON PAUSE -");
145     }
146
147     @Override
148     public void onStop() {
149         super.onStop();
150         if(D) Log.e(TAG, "-- ON STOP --");
151     }
152
153     @Override
154     public void onDestroy() {
155         super.onDestroy();
156         // Stop the Bluetooth chat services
157         if (mChatService != null) mChatService.stop();
158         if(D) Log.e(TAG, "--- ON DESTROY ---");
159     }
160
161
162
163     private void setupChat() {
164         Log.d(TAG, "setupChat()");
165
166         mSerialView = (TextView) findViewById(R.id.in);
167         mSerialView.setMovementMethod(new ScrollingMovementMethod());
168         mSerialView.setClickable(false);
169         mSerialView.setLongClickable(false);
170
171         // Initialize the compose field with a listener for the return key
172         mOutEditText = (EditText) findViewById(R.id.edit_text_out);
173         mOutEditText.setOnEditorActionListener(mWriteListener);
174
175         // Initialize the send button with a listener that for click events
176         mSendButton = (Button) findViewById(R.id.button_send);
177         mSendButton.setOnClickListener(new OnClickListener() {
178             public void onClick(View v) {
179                 // Send a message using content of the edit text widget
180                 TextView view = (TextView) findViewById(R.id.edit_text_out);
181                 String message = view.getText().toString();
182                 sendMessage(message);
183             }
184         });
185
186         // Initialize the BluetoothChatService to perform bluetooth connections
187         mChatService = new BluetoothChatService(this, mHandler);
188
189         // Initialize the buffer for outgoing messages
190         mOutStringBuffer = new StringBuffer("");
191     }
192
193     /**
194      * Sends a message.
195      * @param message  A string of text to send.
196      */
197     private void sendMessage(String message) {
198         // Check that we're actually connected before trying anything
199         if (mChatService.getState() != BluetoothChatService.STATE_CONNECTED) {
200             Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT).show();
201             return;
202         }
203
204         // Check that there's actually something to send
205         if (message.length() > 0) {
206             // Get the message bytes and tell the BluetoothChatService to write
207             byte[] send = message.getBytes();
208             mChatService.write(send);
209
210             // Reset out string buffer to zero and clear the edit text field
211             mOutStringBuffer.setLength(0);
212             mOutEditText.setText(mOutStringBuffer);
213         }
214     }
215
216     // The action listener for the EditText widget, to listen for the return key
217     private TextView.OnEditorActionListener mWriteListener =
218         new TextView.OnEditorActionListener() {
219         public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {
220             // If the action is a key-up event on the return key, send the message
221             if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_UP) {
222                 String message = view.getText().toString();
223                 sendMessage(message);
224             }
225             if(D) Log.i(TAG, "END onEditorAction");
226             return true;
227         }
228     };
229
230     // The Handler that gets information back from the BluetoothChatService
231     private final Handler mHandler = new Handler() {
232         @Override
233         public void handleMessage(Message msg) {
234             switch (msg.what) {
235             case MESSAGE_STATE_CHANGE:
236                 if(D) Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
237                 switch (msg.arg1) {
238                 case BluetoothChatService.STATE_CONNECTED:
239                     mTitle.setText(R.string.title_connected_to);
240                     mTitle.append(mConnectedDeviceName);
241                     mSerialView.setText("");
242                     break;
243                 case BluetoothChatService.STATE_CONNECTING:
244                     mTitle.setText(R.string.title_connecting);
245                     break;
246                 case BluetoothChatService.STATE_READY:
247                 case BluetoothChatService.STATE_NONE:
248                     mTitle.setText(R.string.title_not_connected);
249                     break;
250                 }
251                 break;
252             case MESSAGE_WRITE:
253                 byte[] writeBuf = (byte[]) msg.obj;
254                 // construct a string from the buffer
255                 String writeMessage = new String(writeBuf);
256                 mSerialView.append(writeMessage + '\n');
257                 break;
258             case MESSAGE_READ:
259                 byte[] readBuf = (byte[]) msg.obj;
260                 // construct a string from the valid bytes in the buffer
261                 String readMessage = new String(readBuf, 0, msg.arg1);
262                 mSerialView.append(readMessage);
263                 break;
264             case MESSAGE_DEVICE_NAME:
265                 // save the connected device's name
266                 mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);
267                 Toast.makeText(getApplicationContext(), "Connected to "
268                                + mConnectedDeviceName, Toast.LENGTH_SHORT).show();
269                 break;
270             case MESSAGE_TOAST:
271                 Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST),
272                                Toast.LENGTH_SHORT).show();
273                 break;
274             }
275         }
276     };
277
278     public void onActivityResult(int requestCode, int resultCode, Intent data) {
279         if(D) Log.d(TAG, "onActivityResult " + resultCode);
280         switch (requestCode) {
281         case REQUEST_CONNECT_DEVICE:
282             // When DeviceListActivity returns with a device to connect to
283             if (resultCode == Activity.RESULT_OK) {
284                 connectDevice(data);
285             }
286             break;
287         case REQUEST_ENABLE_BT:
288             // When the request to enable Bluetooth returns
289             if (resultCode == Activity.RESULT_OK) {
290                 // Bluetooth is now enabled, so set up a chat session
291                 setupChat();
292             } else {
293                 // User did not enable Bluetooth or an error occured
294                 Log.d(TAG, "BT not enabled");
295                 Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();
296                 finish();
297             }
298         }
299     }
300
301     private void connectDevice(Intent data) {
302         // Get the device MAC address
303         String address = data.getExtras()
304             .getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
305         // Get the BLuetoothDevice object
306         BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
307         // Attempt to connect to the device
308         mChatService.connect(device);
309     }
310
311     @Override
312     public boolean onCreateOptionsMenu(Menu menu) {
313         MenuInflater inflater = getMenuInflater();
314         inflater.inflate(R.menu.option_menu, menu);
315         return true;
316     }
317
318     @Override
319     public boolean onOptionsItemSelected(MenuItem item) {
320         Intent serverIntent = null;
321         switch (item.getItemId()) {
322         case R.id.telemetry_service_control:
323             serverIntent = new Intent(this, TelemetryServiceActivities.Controller.class);
324             startActivity(serverIntent);
325             return true;
326         case R.id.telemetry_service_bind:
327             serverIntent = new Intent(this, TelemetryServiceActivities.Binding.class);
328             startActivity(serverIntent);
329             return true;
330         case R.id.connect_scan:
331             // Launch the DeviceListActivity to see devices and do scan
332             serverIntent = new Intent(this, DeviceListActivity.class);
333             startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
334             return true;
335         }
336         return false;
337     }
338
339 }