| /* |
| * Copyright (C) 2007 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.cts.verifier.sensors; |
| |
| import android.app.Activity; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.hardware.Camera; |
| import android.hardware.Sensor; |
| import android.hardware.SensorEvent; |
| import android.hardware.SensorEventListener; |
| import android.hardware.SensorManager; |
| import android.media.AudioManager; |
| import android.media.CamcorderProfile; |
| import android.media.MediaRecorder; |
| import android.media.SoundPool; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.os.Environment; |
| import android.util.JsonWriter; |
| import android.util.Log; |
| import android.view.Window; |
| import android.widget.ImageView; |
| import android.widget.Toast; |
| |
| import com.android.cts.verifier.R; |
| |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStreamWriter; |
| import java.text.SimpleDateFormat; |
| import java.util.Date; |
| |
| |
| // ---------------------------------------------------------------------- |
| |
| /** |
| * An activity that does recording of the camera video and rotation vector data at the same time. |
| */ |
| public class RVCVRecordActivity extends Activity { |
| private static final String TAG = "RVCVRecordActivity"; |
| private static final boolean LOCAL_LOGV = false; |
| |
| private MotionIndicatorView mIndicatorView; |
| |
| private SoundPool mSoundPool; |
| private int [] mSoundPoolLookup; |
| |
| private File mRecordDir; |
| private RecordProcedureController mController; |
| private VideoRecorder mVideoRecorder; |
| private RVSensorLogger mRVSensorLogger; |
| private CoverageManager mCoverManager; |
| private CameraPreviewer mPreviewer; |
| |
| public static final int AXIS_NONE = 0; |
| public static final int AXIS_ALL = SensorManager.AXIS_X + |
| SensorManager.AXIS_Y + |
| SensorManager.AXIS_Z; |
| |
| // For Rotation Vector algorithm research use |
| private final static boolean LOG_RAW_SENSORS = false; |
| private RawSensorLogger mRawSensorLogger; |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| // Hide the window title. |
| requestWindowFeature(Window.FEATURE_NO_TITLE); |
| |
| // inflate xml |
| setContentView(R.layout.cam_preview_overlay); |
| |
| // locate views |
| mIndicatorView = (MotionIndicatorView) findViewById(R.id.cam_indicator); |
| |
| initStoragePath(); |
| } |
| |
| @Override |
| protected void onPause() { |
| super.onPause(); |
| mController.quit(); |
| |
| mPreviewer.end(); |
| endSoundPool(); |
| } |
| |
| @Override |
| protected void onResume() { |
| super.onResume(); |
| // delay the initialization as much as possible |
| init(); |
| } |
| |
| /** display toast message |
| * |
| * @param msg Message content |
| */ |
| private void message(String msg) { |
| |
| Context context = getApplicationContext(); |
| int duration = Toast.LENGTH_SHORT; |
| |
| Toast toast = Toast.makeText(context, msg, duration); |
| toast.show(); |
| } |
| |
| /** |
| * Initialize components |
| * |
| */ |
| private void init() { |
| mPreviewer = new CameraPreviewer(); |
| mPreviewer.init(); |
| |
| mCoverManager = new CoverageManager(); |
| mIndicatorView.setDataProvider( |
| mCoverManager.getAxis(SensorManager.AXIS_X), |
| mCoverManager.getAxis(SensorManager.AXIS_Y), |
| mCoverManager.getAxis(SensorManager.AXIS_Z) ); |
| |
| initSoundPool(); |
| mRVSensorLogger = new RVSensorLogger(this); |
| |
| mVideoRecorder = new VideoRecorder(mPreviewer.getCamera()); |
| |
| if (LOG_RAW_SENSORS) { |
| mRawSensorLogger = new RawSensorLogger(mRecordDir); |
| } |
| |
| mController = new RecordProcedureController(this); |
| } |
| |
| /** |
| * Notify recording is completed. This is the successful exit. |
| */ |
| public void notifyComplete() { |
| message("Capture completed!"); |
| |
| Uri resultUri = Uri.fromFile(mRecordDir); |
| Intent result = new Intent(); |
| result.setData(resultUri); |
| setResult(Activity.RESULT_OK, result); |
| |
| finish(); |
| } |
| |
| /** |
| * Notify the user what to do next in text |
| * |
| * @param axis SensorManager.AXIS_X or SensorManager.AXIS_Y or SensorManager.AXIS_Z |
| */ |
| private void notifyPrompt(int axis) { |
| // It is not XYZ because of earlier design have different definition of |
| // X and Y |
| final String axisName = "YXZ"; |
| |
| message("Manipulate the device in " + axisName.charAt(axis-1) + " axis (as illustrated) about the pattern."); |
| } |
| |
| /** |
| * Ask indicator view to redraw |
| */ |
| private void redrawIndicator() { |
| mIndicatorView.invalidate(); |
| } |
| |
| /** |
| * Switch to a different axis for display and logging |
| * @param axis |
| */ |
| private void switchAxis(int axis) { |
| ImageView imageView = (ImageView) findViewById(R.id.cam_overlay); |
| |
| final int [] prompts = {R.drawable.prompt_x, R.drawable.prompt_y, R.drawable.prompt_z}; |
| |
| if (axis >=SensorManager.AXIS_X && axis <=SensorManager.AXIS_Z) { |
| imageView.setImageResource(prompts[axis-1]); |
| mIndicatorView.enableAxis(axis); |
| mRVSensorLogger.updateRegister(mCoverManager.getAxis(axis), axis); |
| notifyPrompt(axis); |
| } else { |
| imageView.setImageDrawable(null); |
| mIndicatorView.enableAxis(AXIS_NONE); |
| } |
| redrawIndicator(); |
| } |
| |
| /** |
| * Asynchronized way to call switchAxis. Use this if caller is not on UI thread. |
| * @param axis @param axis SensorManager.AXIS_X or SensorManager.AXIS_Y or SensorManager.AXIS_Z |
| */ |
| public void switchAxisAsync(int axis) { |
| // intended to be called from a non-UI thread |
| final int fAxis = axis; |
| runOnUiThread(new Runnable() { |
| public void run() { |
| // UI code goes here |
| switchAxis(fAxis); |
| } |
| }); |
| } |
| |
| /** |
| * Initialize sound pool for user notification |
| */ |
| private void initSoundPool() { |
| final int MAX_STREAM = 10; |
| int i=0; |
| mSoundPool = new SoundPool(MAX_STREAM, AudioManager.STREAM_MUSIC, 0); |
| mSoundPoolLookup = new int[MAX_STREAM]; |
| |
| // TODO: add different sound into this |
| mSoundPoolLookup[i++] = mSoundPool.load(this, R.raw.next_axis, 1); |
| mSoundPoolLookup[i++] = mSoundPool.load(this, R.raw.next_axis, 1); |
| mSoundPoolLookup[i++] = mSoundPool.load(this, R.raw.next_axis, 1); |
| |
| } |
| private void endSoundPool() { |
| mSoundPool.release(); |
| } |
| |
| /** |
| * Play notify sound to user |
| * @param id ID of the sound to be played |
| */ |
| public void playNotifySound(int id) { |
| mSoundPool.play(mSoundPoolLookup[id], 1, 1, 0, 0, 1); |
| } |
| |
| /** |
| * Start the sensor recording |
| */ |
| public void startRecordSensor() { |
| mRVSensorLogger.init(); |
| if (LOG_RAW_SENSORS) { |
| mRawSensorLogger.init(); |
| } |
| } |
| |
| /** |
| * Stop the sensor recording |
| */ |
| public void stopRecordSensor() { |
| mRVSensorLogger.end(); |
| if (LOG_RAW_SENSORS) { |
| mRawSensorLogger.end(); |
| } |
| } |
| |
| /** |
| * Start video recording |
| */ |
| public void startRecordVideo() { |
| mVideoRecorder.init(); |
| } |
| |
| /** |
| * Stop video recording |
| */ |
| public void stopRecordVideo() { |
| mVideoRecorder.end(); |
| } |
| |
| /** |
| * Wait until a sensor recording for a certain axis is fully covered |
| * @param axis |
| */ |
| public void waitUntilCovered(int axis) { |
| mCoverManager.waitUntilCovered(axis); |
| } |
| |
| |
| /** |
| * |
| */ |
| private void initStoragePath() { |
| File rxcvRecDataDir = new File(Environment.getExternalStorageDirectory(),"RVCVRecData"); |
| |
| // Create the storage directory if it does not exist |
| if (! rxcvRecDataDir.exists()) { |
| if (! rxcvRecDataDir.mkdirs()) { |
| Log.e(TAG, "failed to create main data directory"); |
| } |
| } |
| |
| mRecordDir = new File(rxcvRecDataDir, new SimpleDateFormat("yyMMdd-hhmmss").format(new Date())); |
| |
| if (! mRecordDir.mkdirs()) { |
| Log.e(TAG, "failed to create rec data directory"); |
| } |
| } |
| |
| /** |
| * Get the sensor log file path |
| * @return Path of the sensor log file |
| */ |
| public String getSensorLogFilePath() { |
| return new File(mRecordDir, "sensor.log").getPath(); |
| } |
| |
| /** |
| * Get the video recording file path |
| * @return Path of the video recording file |
| */ |
| public String getVideoRecFilePath() { |
| return new File(mRecordDir, "video.mp4").getPath(); |
| } |
| |
| /** |
| * Write out important camera/video information to a JSON file |
| * @param width width of frame |
| * @param height height of frame |
| * @param frameRate frame rate in fps |
| * @param fovW field of view in width direction |
| * @param fovH field of view in height direction |
| */ |
| public void writeVideoMetaInfo(int width, int height, float frameRate, float fovW, float fovH) { |
| try { |
| JsonWriter writer = |
| new JsonWriter( |
| new OutputStreamWriter( |
| new FileOutputStream( |
| new File(mRecordDir, "videometa.json").getPath() |
| ) |
| ) |
| ); |
| writer.beginObject(); |
| writer.name("fovW").value(fovW); |
| writer.name("fovH").value(fovH); |
| writer.name("width").value(width); |
| writer.name("height").value(height); |
| writer.name("frameRate").value(frameRate); |
| writer.endObject(); |
| |
| writer.close(); |
| }catch (FileNotFoundException e) { |
| // Not very likely to happen |
| e.printStackTrace(); |
| }catch (IOException e) { |
| // do nothing |
| e.printStackTrace(); |
| Log.e(TAG, "Writing video meta data failed."); |
| } |
| } |
| |
| /** |
| * Camera preview control class |
| */ |
| class CameraPreviewer { |
| private Camera mCamera; |
| |
| CameraPreviewer() { |
| try { |
| mCamera = Camera.open(); // attempt to get a default Camera instance |
| } |
| catch (Exception e) { |
| // Camera is not available (in use or does not exist) |
| Log.e(TAG, "Cannot obtain Camera!"); |
| } |
| } |
| |
| /** |
| * Get the camera to be previewed |
| * @return Reference to Camera used |
| */ |
| public Camera getCamera() { |
| return mCamera; |
| } |
| |
| /** |
| * Setup the camera |
| */ |
| public void init() { |
| if (mCamera != null) { |
| double alpha = mCamera.getParameters().getHorizontalViewAngle()*Math.PI/180.0; |
| int width = 1920; |
| double fx = width/2/Math.tan(alpha/2.0); |
| |
| if (LOCAL_LOGV) Log.v(TAG, "View angle=" |
| + mCamera.getParameters().getHorizontalViewAngle() +" Estimated fx = "+fx); |
| |
| RVCVCameraPreview cameraPreview = |
| (RVCVCameraPreview) findViewById(R.id.cam_preview); |
| cameraPreview.init(mCamera); |
| } else { |
| message("Cannot open camera!"); |
| finish(); |
| } |
| } |
| |
| /** |
| * End the camera preview |
| */ |
| public void end() { |
| if (mCamera != null) { |
| mCamera.release(); // release the camera for other applications |
| mCamera = null; |
| } |
| } |
| } |
| |
| /** |
| * Manage a set of RangeCoveredRegister objects |
| */ |
| class CoverageManager { |
| // settings |
| private final int MAX_TILT_ANGLE = 60; // +/- 60 |
| //private final int REQUIRED_TILT_ANGLE = 50; // +/- 50 |
| private final int TILT_ANGLE_STEP = 5; // 5 degree(s) per step |
| private final int YAW_ANGLE_STEP = 10; // 10 degree(s) per step |
| |
| RangeCoveredRegister[] mAxisCovered; |
| |
| CoverageManager() { |
| mAxisCovered = new RangeCoveredRegister[3]; |
| // X AXIS |
| mAxisCovered[0] = new RangeCoveredRegister(-MAX_TILT_ANGLE, +MAX_TILT_ANGLE, TILT_ANGLE_STEP); |
| // Y AXIS |
| mAxisCovered[1] = new RangeCoveredRegister(-MAX_TILT_ANGLE, +MAX_TILT_ANGLE, TILT_ANGLE_STEP); |
| // Z AXIS |
| mAxisCovered[2] = new RangeCoveredRegister(YAW_ANGLE_STEP); |
| } |
| |
| public RangeCoveredRegister getAxis(int axis) { |
| // SensorManager.AXIS_X = 1, need offset -1 for mAxisCovered array |
| return mAxisCovered[axis-1]; |
| } |
| |
| public void waitUntilCovered(int axis) { |
| // SensorManager.AXIS_X = 1, need offset -1 for mAxisCovered array |
| while(!mAxisCovered[axis-1].isFullyCovered()) { |
| try { |
| Thread.sleep(500); |
| } catch (InterruptedException e) { |
| if (LOCAL_LOGV) { |
| Log.v(TAG, "waitUntilCovered axis = "+ axis + " is interrupted"); |
| } |
| } |
| } |
| } |
| } |
| //////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * A class controls the video recording |
| */ |
| class VideoRecorder |
| { |
| private MediaRecorder mRecorder; |
| private Camera mCamera; |
| private boolean mRunning = false; |
| |
| private int [] mPreferredProfiles = { CamcorderProfile.QUALITY_480P, // smaller -> faster |
| CamcorderProfile.QUALITY_720P, |
| CamcorderProfile.QUALITY_1080P, |
| CamcorderProfile.QUALITY_HIGH // existence guaranteed |
| }; |
| |
| |
| VideoRecorder(Camera camera) { |
| mCamera = camera; |
| } |
| |
| /** |
| * Initialize and start recording |
| */ |
| public void init() { |
| float fovW = mCamera.getParameters().getHorizontalViewAngle(); |
| float fovH = mCamera.getParameters().getVerticalViewAngle(); |
| |
| mRecorder = new MediaRecorder(); |
| mCamera.unlock(); |
| mRecorder.setCamera(mCamera); |
| |
| mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); |
| mRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT); |
| |
| CamcorderProfile profile = null; |
| for (int i: mPreferredProfiles) { |
| if (CamcorderProfile.hasProfile(i)) { |
| profile = CamcorderProfile.get(i); |
| mRecorder.setProfile(profile); |
| break; |
| } |
| } |
| |
| writeVideoMetaInfo(profile.videoFrameWidth, profile.videoFrameHeight, |
| profile.videoFrameRate, fovW, fovH); |
| |
| try { |
| mRecorder.setOutputFile(getVideoRecFilePath()); |
| mRecorder.prepare(); |
| } catch (IOException e) { |
| Log.e(TAG, "Preparation for recording failed."); |
| return; |
| } |
| |
| try { |
| mRecorder.start(); |
| } catch (RuntimeException e) { |
| Log.e(TAG, "Starting recording failed."); |
| mRecorder.reset(); |
| mRecorder.release(); |
| mCamera.lock(); |
| return; |
| } |
| mRunning = true; |
| } |
| |
| /** |
| * Stop recording |
| */ |
| public void end() { |
| if (mRunning) { |
| try { |
| mRecorder.stop(); |
| mRecorder.reset(); |
| mRecorder.release(); |
| mCamera.lock(); |
| } catch (RuntimeException e) { |
| e.printStackTrace(); |
| Log.e(TAG, "Runtime error in stopping recording."); |
| } |
| } |
| mRecorder = null; |
| } |
| |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * Log all raw sensor readings, for Rotation Vector sensor algorithms research |
| */ |
| class RawSensorLogger implements SensorEventListener { |
| private final String TAG = "RawSensorLogger"; |
| |
| private final static int SENSOR_RATE = SensorManager.SENSOR_DELAY_FASTEST; |
| private File mRecPath; |
| |
| SensorManager mSensorManager; |
| Sensor mAccSensor, mGyroSensor, mMagSensor; |
| OutputStreamWriter mAccLogWriter, mGyroLogWriter, mMagLogWriter; |
| |
| private float[] mRTemp = new float[16]; |
| |
| RawSensorLogger(File recPath) { |
| mRecPath = recPath; |
| } |
| |
| /** |
| * Initialize and start recording |
| */ |
| public void init() { |
| mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE); |
| |
| mAccSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); |
| mGyroSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED); |
| mMagSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED); |
| |
| mSensorManager.registerListener(this, mAccSensor, SENSOR_RATE); |
| mSensorManager.registerListener(this, mGyroSensor, SENSOR_RATE); |
| mSensorManager.registerListener(this, mMagSensor, SENSOR_RATE); |
| |
| try { |
| mAccLogWriter= new OutputStreamWriter( |
| new FileOutputStream(new File(mRecPath, "raw_acc.log"))); |
| mGyroLogWriter= new OutputStreamWriter( |
| new FileOutputStream(new File(mRecPath, "raw_uncal_gyro.log"))); |
| mMagLogWriter= new OutputStreamWriter( |
| new FileOutputStream(new File(mRecPath, "raw_uncal_mag.log"))); |
| |
| } catch (FileNotFoundException e) { |
| Log.e(TAG, "Sensor log file open failed: " + e.toString()); |
| } |
| } |
| |
| /** |
| * Stop recording and clean up |
| */ |
| public void end() { |
| mSensorManager.flush(this); |
| mSensorManager.unregisterListener(this); |
| |
| try { |
| if (mAccLogWriter != null) { |
| OutputStreamWriter writer = mAccLogWriter; |
| mAccLogWriter = null; |
| writer.close(); |
| } |
| if (mGyroLogWriter != null) { |
| OutputStreamWriter writer = mGyroLogWriter; |
| mGyroLogWriter = null; |
| writer.close(); |
| } |
| if (mMagLogWriter != null) { |
| OutputStreamWriter writer = mMagLogWriter; |
| mMagLogWriter = null; |
| writer.close(); |
| } |
| |
| } catch (IOException e) { |
| Log.e(TAG, "Sensor log file close failed: " + e.toString()); |
| } |
| } |
| |
| @Override |
| public void onAccuracyChanged(Sensor sensor, int i) { |
| // do not care |
| } |
| |
| @Override |
| public void onSensorChanged(SensorEvent event) { |
| OutputStreamWriter writer=null; |
| switch(event.sensor.getType()) { |
| case Sensor.TYPE_ACCELEROMETER: |
| writer = mAccLogWriter; |
| break; |
| case Sensor.TYPE_GYROSCOPE_UNCALIBRATED: |
| writer = mGyroLogWriter; |
| break; |
| case Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED: |
| writer = mMagLogWriter; |
| break; |
| |
| } |
| if (writer!=null) { |
| float[] data = event.values; |
| try { |
| if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { |
| writer.write(String.format("%d %f %f %f\r\n", |
| event.timestamp, data[0], data[1], data[2])); |
| }else // TYPE_GYROSCOPE_UNCALIBRATED and TYPE_MAGNETIC_FIELD_UNCALIBRATED |
| { |
| writer.write(String.format("%d %f %f %f %f %f %f\r\n", event.timestamp, |
| data[0], data[1], data[2], data[3], data[4], data[5])); |
| } |
| }catch (IOException e) |
| { |
| Log.e(TAG, "Write to raw sensor log file failed."); |
| } |
| |
| } |
| } |
| } |
| |
| /** |
| * Rotation sensor logger class |
| */ |
| class RVSensorLogger implements SensorEventListener { |
| private final String TAG = "RVSensorLogger"; |
| |
| private final static int SENSOR_RATE = SensorManager.SENSOR_DELAY_FASTEST; |
| RangeCoveredRegister mRegister; |
| int mAxis; |
| RVCVRecordActivity mActivity; |
| |
| SensorManager mSensorManager; |
| Sensor mRVSensor; |
| OutputStreamWriter mLogWriter; |
| |
| private float[] mRTemp = new float[16]; |
| |
| RVSensorLogger(RVCVRecordActivity activity) { |
| mActivity = activity; |
| } |
| |
| /** |
| * Initialize and start recording |
| */ |
| public void init() { |
| mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE); |
| mRVSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR); |
| mSensorManager.registerListener(this, mRVSensor, SENSOR_RATE); |
| |
| try { |
| mLogWriter= new OutputStreamWriter( |
| new FileOutputStream(mActivity.getSensorLogFilePath())); |
| } catch (FileNotFoundException e) { |
| Log.e(TAG, "Sensor log file open failed: " + e.toString()); |
| } |
| } |
| |
| /** |
| * Stop recording and clean up |
| */ |
| public void end() { |
| mSensorManager.flush(this); |
| mSensorManager.unregisterListener(this); |
| |
| try { |
| if (mLogWriter != null) { |
| OutputStreamWriter writer = mLogWriter; |
| mLogWriter = null; |
| writer.close(); |
| } |
| } catch (IOException e) { |
| Log.e(TAG, "Sensor log file close failed: " + e.toString()); |
| } |
| |
| updateRegister(null, AXIS_NONE); |
| } |
| |
| private void onNewData(float[] data, long timestamp) { |
| // LOG |
| try { |
| if (mLogWriter != null) { |
| mLogWriter.write(String.format("%d %f %f %f %f\r\n", timestamp, |
| data[3], data[0], data[1], data[2])); |
| } |
| } catch (IOException e) { |
| Log.e(TAG, "Sensor log file write failed: " + e.toString()); |
| } |
| |
| // Update UI |
| if (mRegister != null) { |
| int d = 0; |
| int dx, dy, dz; |
| boolean valid = false; |
| SensorManager.getRotationMatrixFromVector(mRTemp, data); |
| |
| dx = (int)(Math.asin(mRTemp[8])*(180.0/Math.PI)); |
| dy = (int)(Math.asin(mRTemp[9])*(180.0/Math.PI)); |
| dz = (int)((Math.atan2(mRTemp[4], mRTemp[0])+Math.PI)*(180.0/Math.PI)); |
| |
| switch(mAxis) { |
| case SensorManager.AXIS_X: |
| d = dx; |
| valid = (Math.abs(dy) < 30); |
| break; |
| case SensorManager.AXIS_Y: |
| d = dy; |
| valid = (Math.abs(dx) < 30); |
| break; |
| case SensorManager.AXIS_Z: |
| d = dz; |
| valid = (Math.abs(dx) < 20 && Math.abs(dy) < 20); |
| break; |
| } |
| |
| if (valid) { |
| mRegister.update(d); |
| mActivity.redrawIndicator(); |
| } |
| } |
| |
| } |
| |
| public void updateRegister(RangeCoveredRegister reg, int axis) { |
| mRegister = reg; |
| mAxis = axis; |
| } |
| |
| |
| @Override |
| public void onAccuracyChanged(Sensor sensor, int i) { |
| // do not care |
| } |
| |
| @Override |
| public void onSensorChanged(SensorEvent event) { |
| if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) { |
| onNewData(event.values, event.timestamp); |
| } |
| } |
| } |
| |
| |
| //////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * Controls the over all logic of record procedure: first x-direction, then y-direction and |
| * then z-direction. |
| */ |
| class RecordProcedureController implements Runnable { |
| private static final boolean LOCAL_LOGV = false; |
| |
| private final RVCVRecordActivity mActivity; |
| private Thread mThread = null; |
| |
| RecordProcedureController(RVCVRecordActivity activity) { |
| mActivity = activity; |
| mThread = new Thread(this); |
| mThread.start(); |
| } |
| |
| /** |
| * Run the record procedure |
| */ |
| public void run() { |
| if (LOCAL_LOGV) Log.v(TAG, "Controller Thread Started."); |
| //start recording & logging |
| delay(2000); |
| |
| init(); |
| if (LOCAL_LOGV) Log.v(TAG, "Controller Thread init() finished."); |
| |
| // test 3 axis |
| // It is in YXZ order because UI element design use opposite definition |
| // of XY axis. To ensure the user see X Y Z, it is flipped here. |
| recordAxis(SensorManager.AXIS_Y); |
| if (LOCAL_LOGV) Log.v(TAG, "Controller Thread axis 0 finished."); |
| |
| recordAxis(SensorManager.AXIS_X); |
| if (LOCAL_LOGV) Log.v(TAG, "Controller Thread axis 1 finished."); |
| |
| recordAxis(SensorManager.AXIS_Z); |
| if (LOCAL_LOGV) Log.v(TAG, "Controller Thread axis 2 finished."); |
| |
| delay(1000); |
| end(); |
| if (LOCAL_LOGV) Log.v(TAG, "Controller Thread End."); |
| } |
| |
| private void delay(int milli) { |
| try{ |
| Thread.sleep(milli); |
| } catch(InterruptedException e) { |
| if (LOCAL_LOGV) Log.v(TAG, "Controller Thread Interrupted."); |
| } |
| } |
| private void init() { |
| // start video recording |
| mActivity.startRecordVideo(); |
| |
| // start sensor logging & listening |
| mActivity.startRecordSensor(); |
| } |
| |
| private void end() { |
| // stop video recording |
| mActivity.stopRecordVideo(); |
| |
| // stop sensor logging |
| mActivity.stopRecordSensor(); |
| |
| // notify ui complete |
| runOnUiThread(new Runnable(){ |
| public void run() { |
| mActivity.notifyComplete(); |
| } |
| }); |
| } |
| |
| private void recordAxis(int axis) { |
| // delay 2 seconds? |
| delay(1000); |
| |
| // change ui |
| mActivity.switchAxisAsync(axis); |
| |
| // play start sound |
| mActivity.playNotifySound(0); |
| |
| // wait until axis covered |
| mActivity.waitUntilCovered(axis); |
| |
| // play stop sound |
| mActivity.playNotifySound(1); |
| } |
| |
| /** |
| * Force quit |
| */ |
| public void quit() { |
| mThread.interrupt(); |
| try { |
| if (LOCAL_LOGV) Log.v(TAG, "Wait for controller to end"); |
| |
| // stop video recording |
| mActivity.stopRecordVideo(); |
| |
| // stop sensor logging |
| mActivity.stopRecordSensor(); |
| |
| } catch (Exception e) |
| { |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| } |