| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| |
| package com.android.tools.sdkcontroller.activities; |
| |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import android.os.Bundle; |
| import android.os.Message; |
| import android.util.Log; |
| import android.view.KeyEvent; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.View.OnFocusChangeListener; |
| import android.view.View.OnKeyListener; |
| import android.widget.CheckBox; |
| import android.widget.CompoundButton; |
| import android.widget.TableLayout; |
| import android.widget.TableRow; |
| import android.widget.TextView; |
| |
| import com.android.tools.sdkcontroller.R; |
| import com.android.tools.sdkcontroller.handlers.SensorChannel; |
| import com.android.tools.sdkcontroller.handlers.SensorChannel.MonitoredSensor; |
| import com.android.tools.sdkcontroller.lib.Channel; |
| import com.android.tools.sdkcontroller.service.ControllerService.ControllerBinder; |
| import com.android.tools.sdkcontroller.service.ControllerService.ControllerListener; |
| |
| /** |
| * Activity that displays and controls the sensors from {@link SensorChannel}. |
| * For each sensor it displays a checkbox that is enabled if the sensor is supported |
| * by the emulator. The user can select whether the sensor is active. It also displays |
| * data from the sensor when available. |
| */ |
| public class SensorActivity extends BaseBindingActivity |
| implements android.os.Handler.Callback { |
| |
| @SuppressWarnings("hiding") |
| public static String TAG = SensorActivity.class.getSimpleName(); |
| private static boolean DEBUG = true; |
| |
| private static final int MSG_UPDATE_ACTUAL_HZ = 0x31415; |
| |
| private TableLayout mTableLayout; |
| private TextView mTextError; |
| private TextView mTextStatus; |
| private TextView mTextTargetHz; |
| private TextView mTextActualHz; |
| private SensorChannel mSensorHandler; |
| |
| private final Map<MonitoredSensor, DisplayInfo> mDisplayedSensors = |
| new HashMap<SensorChannel.MonitoredSensor, SensorActivity.DisplayInfo>(); |
| private final android.os.Handler mUiHandler = new android.os.Handler(this); |
| private int mTargetSampleRate; |
| private long mLastActualUpdateMs; |
| |
| /** Called when the activity is first created. */ |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| setContentView(R.layout.sensors); |
| mTableLayout = (TableLayout) findViewById(R.id.tableLayout); |
| mTextError = (TextView) findViewById(R.id.textError); |
| mTextStatus = (TextView) findViewById(R.id.textStatus); |
| mTextTargetHz = (TextView) findViewById(R.id.textSampleRate); |
| mTextActualHz = (TextView) findViewById(R.id.textActualRate); |
| updateStatus("Waiting for connection"); |
| |
| mTextTargetHz.setOnKeyListener(new OnKeyListener() { |
| @Override |
| public boolean onKey(View v, int keyCode, KeyEvent event) { |
| updateSampleRate(); |
| return false; |
| } |
| }); |
| mTextTargetHz.setOnFocusChangeListener(new OnFocusChangeListener() { |
| @Override |
| public void onFocusChange(View v, boolean hasFocus) { |
| updateSampleRate(); |
| } |
| }); |
| } |
| |
| @Override |
| protected void onResume() { |
| if (DEBUG) Log.d(TAG, "onResume"); |
| // BaseBindingActivity.onResume will bind to the service. |
| super.onResume(); |
| updateError(); |
| } |
| |
| @Override |
| protected void onPause() { |
| if (DEBUG) Log.d(TAG, "onPause"); |
| // BaseBindingActivity.onResume will unbind from (but not stop) the service. |
| super.onPause(); |
| } |
| |
| @Override |
| protected void onDestroy() { |
| if (DEBUG) Log.d(TAG, "onDestroy"); |
| super.onDestroy(); |
| removeSensorUi(); |
| } |
| |
| // ---------- |
| |
| @Override |
| protected void onServiceConnected() { |
| if (DEBUG) Log.d(TAG, "onServiceConnected"); |
| createSensorUi(); |
| } |
| |
| @Override |
| protected void onServiceDisconnected() { |
| if (DEBUG) Log.d(TAG, "onServiceDisconnected"); |
| removeSensorUi(); |
| } |
| |
| @Override |
| protected ControllerListener createControllerListener() { |
| return new SensorsControllerListener(); |
| } |
| |
| // ---------- |
| |
| private class SensorsControllerListener implements ControllerListener { |
| @Override |
| public void onErrorChanged() { |
| runOnUiThread(new Runnable() { |
| @Override |
| public void run() { |
| updateError(); |
| } |
| }); |
| } |
| |
| @Override |
| public void onStatusChanged() { |
| runOnUiThread(new Runnable() { |
| @Override |
| public void run() { |
| ControllerBinder binder = getServiceBinder(); |
| if (binder != null) { |
| boolean connected = binder.isEmuConnected(); |
| mTableLayout.setEnabled(connected); |
| updateStatus(connected ? "Emulated connected" : "Emulator disconnected"); |
| } |
| } |
| }); |
| } |
| } |
| |
| private void createSensorUi() { |
| final LayoutInflater inflater = getLayoutInflater(); |
| |
| if (!mDisplayedSensors.isEmpty()) { |
| removeSensorUi(); |
| } |
| |
| mSensorHandler = (SensorChannel) getServiceBinder().getChannel(Channel.SENSOR_CHANNEL); |
| if (mSensorHandler != null) { |
| mSensorHandler.addUiHandler(mUiHandler); |
| mUiHandler.sendEmptyMessage(MSG_UPDATE_ACTUAL_HZ); |
| |
| assert mDisplayedSensors.isEmpty(); |
| List<MonitoredSensor> sensors = mSensorHandler.getSensors(); |
| for (MonitoredSensor sensor : sensors) { |
| final TableRow row = (TableRow) inflater.inflate(R.layout.sensor_row, |
| mTableLayout, |
| false); |
| mTableLayout.addView(row); |
| mDisplayedSensors.put(sensor, new DisplayInfo(sensor, row)); |
| } |
| } |
| } |
| |
| private void removeSensorUi() { |
| if (mSensorHandler != null) { |
| mSensorHandler.removeUiHandler(mUiHandler); |
| mSensorHandler = null; |
| } |
| mTableLayout.removeAllViews(); |
| for (DisplayInfo info : mDisplayedSensors.values()) { |
| info.release(); |
| } |
| mDisplayedSensors.clear(); |
| } |
| |
| private class DisplayInfo implements CompoundButton.OnCheckedChangeListener { |
| private MonitoredSensor mSensor; |
| private CheckBox mChk; |
| private TextView mVal; |
| |
| public DisplayInfo(MonitoredSensor sensor, TableRow row) { |
| mSensor = sensor; |
| |
| // Initialize displayed checkbox for this sensor, and register |
| // checked state listener for it. |
| mChk = (CheckBox) row.findViewById(R.id.row_checkbox); |
| mChk.setText(sensor.getUiName()); |
| mChk.setEnabled(sensor.isEnabledByEmulator()); |
| mChk.setChecked(sensor.isEnabledByUser()); |
| mChk.setOnCheckedChangeListener(this); |
| |
| // Initialize displayed text box for this sensor. |
| mVal = (TextView) row.findViewById(R.id.row_textview); |
| mVal.setText(sensor.getValue()); |
| } |
| |
| /** |
| * Handles checked state change for the associated CheckBox. If check |
| * box is checked we will register sensor change listener. If it is |
| * unchecked, we will unregister sensor change listener. |
| */ |
| @Override |
| public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { |
| if (mSensor != null) { |
| mSensor.onCheckedChanged(isChecked); |
| } |
| } |
| |
| public void release() { |
| mChk = null; |
| mVal = null; |
| mSensor = null; |
| |
| } |
| |
| public void updateState() { |
| if (mChk != null && mSensor != null) { |
| mChk.setEnabled(mSensor.isEnabledByEmulator()); |
| mChk.setChecked(mSensor.isEnabledByUser()); |
| } |
| } |
| |
| public void updateValue() { |
| if (mVal != null && mSensor != null) { |
| mVal.setText(mSensor.getValue()); |
| } |
| } |
| } |
| |
| /** Implementation of Handler.Callback */ |
| @Override |
| public boolean handleMessage(Message msg) { |
| DisplayInfo info = null; |
| switch (msg.what) { |
| case SensorChannel.SENSOR_STATE_CHANGED: |
| info = mDisplayedSensors.get(msg.obj); |
| if (info != null) { |
| info.updateState(); |
| } |
| break; |
| case SensorChannel.SENSOR_DISPLAY_MODIFIED: |
| info = mDisplayedSensors.get(msg.obj); |
| if (info != null) { |
| info.updateValue(); |
| } |
| if (mSensorHandler != null) { |
| updateStatus(Integer.toString(mSensorHandler.getMsgSentCount()) + " events sent"); |
| |
| // Update the "actual rate" field if the value has changed |
| long ms = mSensorHandler.getActualUpdateMs(); |
| if (ms != mLastActualUpdateMs) { |
| mLastActualUpdateMs = ms; |
| String hz = mLastActualUpdateMs <= 0 ? "--" : |
| Integer.toString((int) Math.ceil(1000. / ms)); |
| mTextActualHz.setText(hz); |
| } |
| } |
| break; |
| case MSG_UPDATE_ACTUAL_HZ: |
| if (mSensorHandler != null) { |
| // Update the "actual rate" field if the value has changed |
| long ms = mSensorHandler.getActualUpdateMs(); |
| if (ms != mLastActualUpdateMs) { |
| mLastActualUpdateMs = ms; |
| String hz = mLastActualUpdateMs <= 0 ? "--" : |
| Integer.toString((int) Math.ceil(1000. / ms)); |
| mTextActualHz.setText(hz); |
| } |
| mUiHandler.sendEmptyMessageDelayed(MSG_UPDATE_ACTUAL_HZ, 1000 /*1s*/); |
| } |
| } |
| return true; // we consumed this message |
| } |
| |
| private void updateStatus(String status) { |
| mTextStatus.setVisibility(status == null ? View.GONE : View.VISIBLE); |
| if (status != null) mTextStatus.setText(status); |
| } |
| |
| private void updateError() { |
| ControllerBinder binder = getServiceBinder(); |
| String error = binder == null ? "" : binder.getServiceError(); |
| if (error == null) { |
| error = ""; |
| } |
| |
| mTextError.setVisibility(error.length() == 0 ? View.GONE : View.VISIBLE); |
| mTextError.setText(error); |
| } |
| |
| private void updateSampleRate() { |
| String str = mTextTargetHz.getText().toString(); |
| try { |
| int hz = Integer.parseInt(str.trim()); |
| |
| // Cap the value. 50 Hz is a reasonable max value for the emulator. |
| if (hz <= 0 || hz > 50) { |
| hz = 50; |
| } |
| |
| if (hz != mTargetSampleRate) { |
| mTargetSampleRate = hz; |
| if (mSensorHandler != null) { |
| mSensorHandler.setUpdateTargetMs(hz <= 0 ? 0 : (int)(1000.0f / hz)); |
| } |
| } |
| } catch (Exception ignore) {} |
| } |
| } |