• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
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
6  * 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, 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
14  * the License.
15  */
16 
17 package com.android.tools.sdkcontroller.activities;
18 
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 
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;
36 
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;
43 
44 /**
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.
49  */
50 public class SensorActivity extends BaseBindingActivity
51         implements android.os.Handler.Callback {
52 
53     @SuppressWarnings("hiding")
54     public static String TAG = SensorActivity.class.getSimpleName();
55     private static boolean DEBUG = true;
56 
57     private static final int MSG_UPDATE_ACTUAL_HZ = 0x31415;
58 
59     private TableLayout mTableLayout;
60     private TextView mTextError;
61     private TextView mTextStatus;
62     private TextView mTextTargetHz;
63     private TextView mTextActualHz;
64     private SensorChannel mSensorHandler;
65 
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;
71 
72     /** Called when the activity is first created. */
73     @Override
onCreate(Bundle savedInstanceState)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");
83 
84         mTextTargetHz.setOnKeyListener(new OnKeyListener() {
85             @Override
86             public boolean onKey(View v, int keyCode, KeyEvent event) {
87                 updateSampleRate();
88                 return false;
89             }
90         });
91         mTextTargetHz.setOnFocusChangeListener(new OnFocusChangeListener() {
92             @Override
93             public void onFocusChange(View v, boolean hasFocus) {
94                 updateSampleRate();
95             }
96         });
97     }
98 
99     @Override
onResume()100     protected void onResume() {
101         if (DEBUG) Log.d(TAG, "onResume");
102         // BaseBindingActivity.onResume will bind to the service.
103         super.onResume();
104         updateError();
105     }
106 
107     @Override
onPause()108     protected void onPause() {
109         if (DEBUG) Log.d(TAG, "onPause");
110         // BaseBindingActivity.onResume will unbind from (but not stop) the service.
111         super.onPause();
112     }
113 
114     @Override
onDestroy()115     protected void onDestroy() {
116         if (DEBUG) Log.d(TAG, "onDestroy");
117         super.onDestroy();
118         removeSensorUi();
119     }
120 
121     // ----------
122 
123     @Override
onServiceConnected()124     protected void onServiceConnected() {
125         if (DEBUG) Log.d(TAG, "onServiceConnected");
126         createSensorUi();
127     }
128 
129     @Override
onServiceDisconnected()130     protected void onServiceDisconnected() {
131         if (DEBUG) Log.d(TAG, "onServiceDisconnected");
132         removeSensorUi();
133     }
134 
135     @Override
createControllerListener()136     protected ControllerListener createControllerListener() {
137         return new SensorsControllerListener();
138     }
139 
140     // ----------
141 
142     private class SensorsControllerListener implements ControllerListener {
143         @Override
onErrorChanged()144         public void onErrorChanged() {
145             runOnUiThread(new Runnable() {
146                 @Override
147                 public void run() {
148                     updateError();
149                 }
150             });
151         }
152 
153         @Override
onStatusChanged()154         public void onStatusChanged() {
155             runOnUiThread(new Runnable() {
156                 @Override
157                 public void run() {
158                     ControllerBinder binder = getServiceBinder();
159                     if (binder != null) {
160                         boolean connected = binder.isEmuConnected();
161                         mTableLayout.setEnabled(connected);
162                         updateStatus(connected ? "Emulated connected" : "Emulator disconnected");
163                     }
164                 }
165             });
166         }
167     }
168 
createSensorUi()169     private void createSensorUi() {
170         final LayoutInflater inflater = getLayoutInflater();
171 
172         if (!mDisplayedSensors.isEmpty()) {
173             removeSensorUi();
174         }
175 
176         mSensorHandler = (SensorChannel) getServiceBinder().getChannel(Channel.SENSOR_CHANNEL);
177         if (mSensorHandler != null) {
178             mSensorHandler.addUiHandler(mUiHandler);
179             mUiHandler.sendEmptyMessage(MSG_UPDATE_ACTUAL_HZ);
180 
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,
185                                                                  mTableLayout,
186                                                                  false);
187                 mTableLayout.addView(row);
188                 mDisplayedSensors.put(sensor, new DisplayInfo(sensor, row));
189             }
190         }
191     }
192 
removeSensorUi()193     private void removeSensorUi() {
194         if (mSensorHandler != null) {
195             mSensorHandler.removeUiHandler(mUiHandler);
196             mSensorHandler = null;
197         }
198         mTableLayout.removeAllViews();
199         for (DisplayInfo info : mDisplayedSensors.values()) {
200             info.release();
201         }
202         mDisplayedSensors.clear();
203     }
204 
205     private class DisplayInfo implements CompoundButton.OnCheckedChangeListener {
206         private MonitoredSensor mSensor;
207         private CheckBox mChk;
208         private TextView mVal;
209 
DisplayInfo(MonitoredSensor sensor, TableRow row)210         public DisplayInfo(MonitoredSensor sensor, TableRow row) {
211             mSensor = sensor;
212 
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);
220 
221             // Initialize displayed text box for this sensor.
222             mVal = (TextView) row.findViewById(R.id.row_textview);
223             mVal.setText(sensor.getValue());
224         }
225 
226         /**
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.
230          */
231         @Override
onCheckedChanged(CompoundButton buttonView, boolean isChecked)232         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
233             if (mSensor != null) {
234                 mSensor.onCheckedChanged(isChecked);
235             }
236         }
237 
release()238         public void release() {
239             mChk = null;
240             mVal = null;
241             mSensor = null;
242 
243         }
244 
updateState()245         public void updateState() {
246             if (mChk != null && mSensor != null) {
247                 mChk.setEnabled(mSensor.isEnabledByEmulator());
248                 mChk.setChecked(mSensor.isEnabledByUser());
249             }
250         }
251 
updateValue()252         public void updateValue() {
253             if (mVal != null && mSensor != null) {
254                 mVal.setText(mSensor.getValue());
255             }
256         }
257     }
258 
259     /** Implementation of Handler.Callback */
260     @Override
handleMessage(Message msg)261     public boolean handleMessage(Message msg) {
262         DisplayInfo info = null;
263         switch (msg.what) {
264         case SensorChannel.SENSOR_STATE_CHANGED:
265             info = mDisplayedSensors.get(msg.obj);
266             if (info != null) {
267                 info.updateState();
268             }
269             break;
270         case SensorChannel.SENSOR_DISPLAY_MODIFIED:
271             info = mDisplayedSensors.get(msg.obj);
272             if (info != null) {
273                 info.updateValue();
274             }
275             if (mSensorHandler != null) {
276                 updateStatus(Integer.toString(mSensorHandler.getMsgSentCount()) + " events sent");
277 
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);
285                 }
286             }
287             break;
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);
297                 }
298                 mUiHandler.sendEmptyMessageDelayed(MSG_UPDATE_ACTUAL_HZ, 1000 /*1s*/);
299             }
300         }
301         return true; // we consumed this message
302     }
303 
updateStatus(String status)304     private void updateStatus(String status) {
305         mTextStatus.setVisibility(status == null ? View.GONE : View.VISIBLE);
306         if (status != null) mTextStatus.setText(status);
307     }
308 
updateError()309     private void updateError() {
310         ControllerBinder binder = getServiceBinder();
311         String error = binder == null ? "" : binder.getServiceError();
312         if (error == null) {
313             error = "";
314         }
315 
316         mTextError.setVisibility(error.length() == 0 ? View.GONE : View.VISIBLE);
317         mTextError.setText(error);
318     }
319 
updateSampleRate()320     private void updateSampleRate() {
321         String str = mTextTargetHz.getText().toString();
322         try {
323             int hz = Integer.parseInt(str.trim());
324 
325             // Cap the value. 50 Hz is a reasonable max value for the emulator.
326             if (hz <= 0 || hz > 50) {
327                 hz = 50;
328             }
329 
330             if (hz != mTargetSampleRate) {
331                 mTargetSampleRate = hz;
332                 if (mSensorHandler != null) {
333                     mSensorHandler.setUpdateTargetMs(hz <= 0 ? 0 : (int)(1000.0f / hz));
334                 }
335             }
336         } catch (Exception ignore) {}
337     }
338 }
339