Home | History | Annotate | Download | only in activities
      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
     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
    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
    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
    115     protected void onDestroy() {
    116         if (DEBUG) Log.d(TAG, "onDestroy");
    117         super.onDestroy();
    118         removeSensorUi();
    119     }
    120 
    121     // ----------
    122 
    123     @Override
    124     protected void onServiceConnected() {
    125         if (DEBUG) Log.d(TAG, "onServiceConnected");
    126         createSensorUi();
    127     }
    128 
    129     @Override
    130     protected void onServiceDisconnected() {
    131         if (DEBUG) Log.d(TAG, "onServiceDisconnected");
    132         removeSensorUi();
    133     }
    134 
    135     @Override
    136     protected ControllerListener createControllerListener() {
    137         return new SensorsControllerListener();
    138     }
    139 
    140     // ----------
    141 
    142     private class SensorsControllerListener implements ControllerListener {
    143         @Override
    144         public void onErrorChanged() {
    145             runOnUiThread(new Runnable() {
    146                 @Override
    147                 public void run() {
    148                     updateError();
    149                 }
    150             });
    151         }
    152 
    153         @Override
    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 
    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 
    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 
    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
    232         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    233             if (mSensor != null) {
    234                 mSensor.onCheckedChanged(isChecked);
    235             }
    236         }
    237 
    238         public void release() {
    239             mChk = null;
    240             mVal = null;
    241             mSensor = null;
    242 
    243         }
    244 
    245         public void updateState() {
    246             if (mChk != null && mSensor != null) {
    247                 mChk.setEnabled(mSensor.isEnabledByEmulator());
    248                 mChk.setChecked(mSensor.isEnabledByUser());
    249             }
    250         }
    251 
    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
    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 
    304     private void updateStatus(String status) {
    305         mTextStatus.setVisibility(status == null ? View.GONE : View.VISIBLE);
    306         if (status != null) mTextStatus.setText(status);
    307     }
    308 
    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 
    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