2 * Copyright (C) 2012 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
17 package com
.android
.tools
.sdkcontroller
.activities
;
19 import java
.util
.HashMap
;
20 import java
.util
.List
;
23 import android
.os
.Bundle
;
24 import android
.os
.Message
;
25 import android
.util
.Log
;
26 import android
.view
.KeyEvent
;
27 import android
.view
.LayoutInflater
;
28 import android
.view
.View
;
29 import android
.view
.View
.OnFocusChangeListener
;
30 import android
.view
.View
.OnKeyListener
;
31 import android
.widget
.CheckBox
;
32 import android
.widget
.CompoundButton
;
33 import android
.widget
.TableLayout
;
34 import android
.widget
.TableRow
;
35 import android
.widget
.TextView
;
37 import com
.android
.tools
.sdkcontroller
.R
;
38 import com
.android
.tools
.sdkcontroller
.handlers
.SensorChannel
;
39 import com
.android
.tools
.sdkcontroller
.handlers
.SensorChannel
.MonitoredSensor
;
40 import com
.android
.tools
.sdkcontroller
.lib
.Channel
;
41 import com
.android
.tools
.sdkcontroller
.service
.ControllerService
.ControllerBinder
;
42 import com
.android
.tools
.sdkcontroller
.service
.ControllerService
.ControllerListener
;
45 * Activity that displays and controls the sensors from {@link SensorChannel}.
46 * For each sensor it displays a checkbox that is enabled if the sensor is supported
47 * by the emulator. The user can select whether the sensor is active. It also displays
48 * data from the sensor when available.
50 public class SensorActivity
extends BaseBindingActivity
51 implements android
.os
.Handler
.Callback
{
53 @SuppressWarnings("hiding")
54 public static String TAG
= SensorActivity
.class.getSimpleName();
55 private static boolean DEBUG
= true;
57 private static final int MSG_UPDATE_ACTUAL_HZ
= 0x31415;
59 private TableLayout mTableLayout
;
60 private TextView mTextError
;
61 private TextView mTextStatus
;
62 private TextView mTextTargetHz
;
63 private TextView mTextActualHz
;
64 private SensorChannel mSensorHandler
;
66 private final Map
<MonitoredSensor
, DisplayInfo
> mDisplayedSensors
=
67 new HashMap
<SensorChannel
.MonitoredSensor
, SensorActivity
.DisplayInfo
>();
68 private final android
.os
.Handler mUiHandler
= new android
.os
.Handler(this);
69 private int mTargetSampleRate
;
70 private long mLastActualUpdateMs
;
72 /** Called when the activity is first created. */
74 public void onCreate(Bundle savedInstanceState
) {
75 super.onCreate(savedInstanceState
);
76 setContentView(R
.layout
.sensors
);
77 mTableLayout
= (TableLayout
) findViewById(R
.id
.tableLayout
);
78 mTextError
= (TextView
) findViewById(R
.id
.textError
);
79 mTextStatus
= (TextView
) findViewById(R
.id
.textStatus
);
80 mTextTargetHz
= (TextView
) findViewById(R
.id
.textSampleRate
);
81 mTextActualHz
= (TextView
) findViewById(R
.id
.textActualRate
);
82 updateStatus("Waiting for connection");
84 mTextTargetHz
.setOnKeyListener(new OnKeyListener() {
86 public boolean onKey(View v
, int keyCode
, KeyEvent event
) {
91 mTextTargetHz
.setOnFocusChangeListener(new OnFocusChangeListener() {
93 public void onFocusChange(View v
, boolean hasFocus
) {
100 protected void onResume() {
101 if (DEBUG
) Log
.d(TAG
, "onResume");
102 // BaseBindingActivity.onResume will bind to the service.
108 protected void onPause() {
109 if (DEBUG
) Log
.d(TAG
, "onPause");
110 // BaseBindingActivity.onResume will unbind from (but not stop) the service.
115 protected void onDestroy() {
116 if (DEBUG
) Log
.d(TAG
, "onDestroy");
124 protected void onServiceConnected() {
125 if (DEBUG
) Log
.d(TAG
, "onServiceConnected");
130 protected void onServiceDisconnected() {
131 if (DEBUG
) Log
.d(TAG
, "onServiceDisconnected");
136 protected ControllerListener
createControllerListener() {
137 return new SensorsControllerListener();
142 private class SensorsControllerListener
implements ControllerListener
{
144 public void onErrorChanged() {
145 runOnUiThread(new Runnable() {
154 public void onStatusChanged() {
155 runOnUiThread(new Runnable() {
158 ControllerBinder binder
= getServiceBinder();
159 if (binder
!= null) {
160 boolean connected
= binder
.isEmuConnected();
161 mTableLayout
.setEnabled(connected
);
162 updateStatus(connected ?
"Emulated connected" : "Emulator disconnected");
169 private void createSensorUi() {
170 final LayoutInflater inflater
= getLayoutInflater();
172 if (!mDisplayedSensors
.isEmpty()) {
176 mSensorHandler
= (SensorChannel
) getServiceBinder().getChannel(Channel
.SENSOR_CHANNEL
);
177 if (mSensorHandler
!= null) {
178 mSensorHandler
.addUiHandler(mUiHandler
);
179 mUiHandler
.sendEmptyMessage(MSG_UPDATE_ACTUAL_HZ
);
181 assert mDisplayedSensors
.isEmpty();
182 List
<MonitoredSensor
> sensors
= mSensorHandler
.getSensors();
183 for (MonitoredSensor sensor
: sensors
) {
184 final TableRow row
= (TableRow
) inflater
.inflate(R
.layout
.sensor_row
,
187 mTableLayout
.addView(row
);
188 mDisplayedSensors
.put(sensor
, new DisplayInfo(sensor
, row
));
193 private void removeSensorUi() {
194 if (mSensorHandler
!= null) {
195 mSensorHandler
.removeUiHandler(mUiHandler
);
196 mSensorHandler
= null;
198 mTableLayout
.removeAllViews();
199 for (DisplayInfo info
: mDisplayedSensors
.values()) {
202 mDisplayedSensors
.clear();
205 private class DisplayInfo
implements CompoundButton
.OnCheckedChangeListener
{
206 private MonitoredSensor mSensor
;
207 private CheckBox mChk
;
208 private TextView mVal
;
210 public DisplayInfo(MonitoredSensor sensor
, TableRow row
) {
213 // Initialize displayed checkbox for this sensor, and register
214 // checked state listener for it.
215 mChk
= (CheckBox
) row
.findViewById(R
.id
.row_checkbox
);
216 mChk
.setText(sensor
.getUiName());
217 mChk
.setEnabled(sensor
.isEnabledByEmulator());
218 mChk
.setChecked(sensor
.isEnabledByUser());
219 mChk
.setOnCheckedChangeListener(this);
221 // Initialize displayed text box for this sensor.
222 mVal
= (TextView
) row
.findViewById(R
.id
.row_textview
);
223 mVal
.setText(sensor
.getValue());
227 * Handles checked state change for the associated CheckBox. If check
228 * box is checked we will register sensor change listener. If it is
229 * unchecked, we will unregister sensor change listener.
232 public void onCheckedChanged(CompoundButton buttonView
, boolean isChecked
) {
233 if (mSensor
!= null) {
234 mSensor
.onCheckedChanged(isChecked
);
238 public void release() {
245 public void updateState() {
246 if (mChk
!= null && mSensor
!= null) {
247 mChk
.setEnabled(mSensor
.isEnabledByEmulator());
248 mChk
.setChecked(mSensor
.isEnabledByUser());
252 public void updateValue() {
253 if (mVal
!= null && mSensor
!= null) {
254 mVal
.setText(mSensor
.getValue());
259 /** Implementation of Handler.Callback */
261 public boolean handleMessage(Message msg
) {
262 DisplayInfo info
= null;
264 case SensorChannel
.SENSOR_STATE_CHANGED
:
265 info
= mDisplayedSensors
.get(msg
.obj
);
270 case SensorChannel
.SENSOR_DISPLAY_MODIFIED
:
271 info
= mDisplayedSensors
.get(msg
.obj
);
275 if (mSensorHandler
!= null) {
276 updateStatus(Integer
.toString(mSensorHandler
.getMsgSentCount()) + " events sent");
278 // Update the "actual rate" field if the value has changed
279 long ms
= mSensorHandler
.getActualUpdateMs();
280 if (ms
!= mLastActualUpdateMs
) {
281 mLastActualUpdateMs
= ms
;
282 String hz
= mLastActualUpdateMs
<= 0 ?
"--" :
283 Integer
.toString((int) Math
.ceil(1000. / ms
));
284 mTextActualHz
.setText(hz
);
288 case MSG_UPDATE_ACTUAL_HZ
:
289 if (mSensorHandler
!= null) {
290 // Update the "actual rate" field if the value has changed
291 long ms
= mSensorHandler
.getActualUpdateMs();
292 if (ms
!= mLastActualUpdateMs
) {
293 mLastActualUpdateMs
= ms
;
294 String hz
= mLastActualUpdateMs
<= 0 ?
"--" :
295 Integer
.toString((int) Math
.ceil(1000. / ms
));
296 mTextActualHz
.setText(hz
);
298 mUiHandler
.sendEmptyMessageDelayed(MSG_UPDATE_ACTUAL_HZ
, 1000 /*1s*/);
301 return true; // we consumed this message
304 private void updateStatus(String status
) {
305 mTextStatus
.setVisibility(status
== null ? View
.GONE
: View
.VISIBLE
);
306 if (status
!= null) mTextStatus
.setText(status
);
309 private void updateError() {
310 ControllerBinder binder
= getServiceBinder();
311 String error
= binder
== null ?
"" : binder
.getServiceError();
316 mTextError
.setVisibility(error
.length() == 0 ? View
.GONE
: View
.VISIBLE
);
317 mTextError
.setText(error
);
320 private void updateSampleRate() {
321 String str
= mTextTargetHz
.getText().toString();
323 int hz
= Integer
.parseInt(str
.trim());
325 // Cap the value. 50 Hz is a reasonable max value for the emulator.
326 if (hz
<= 0 || hz
> 50) {
330 if (hz
!= mTargetSampleRate
) {
331 mTargetSampleRate
= hz
;
332 if (mSensorHandler
!= null) {
333 mSensorHandler
.setUpdateTargetMs(hz
<= 0 ?
0 : (int)(1000.0f
/ hz
));
336 } catch (Exception ignore
) {}