| |
| package com.fairphone.psensor; |
| |
| import android.app.Activity; |
| import android.content.ContentValues; |
| import android.content.Context; |
| import android.content.SharedPreferences; |
| import android.content.pm.PackageInfo; |
| import android.content.pm.PackageManager; |
| import android.database.sqlite.SQLiteDatabase; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.PowerManager; |
| import android.util.Log; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.widget.Button; |
| import android.widget.ProgressBar; |
| import android.widget.TextView; |
| import android.widget.ViewFlipper; |
| |
| import com.fairphone.psensor.CalibrationContract.CalibrationData; |
| |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.io.RandomAccessFile; |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| |
| /** |
| * 1. Hint user block sensor and read value. if less than 230, hint and wait confirm. <BR> |
| * 2. Hint user unblock sensor and read value. if greater than 96, hint and wait confirm. <BR> |
| * 3. Use the value of unblock to do the calibration. value+30 as far, value+60 as near. <BR> |
| * 4. Write far and near value to /persist/sns.reg binary file. <BR> |
| * 5. The file of sns.reg content as "0000100: 0a3c 0000 <near> <far> 6400 6400 01c0 0000" <BR> |
| */ |
| public class CalibrationActivity extends Activity { |
| private static final String TAG = CalibrationActivity.class.getSimpleName(); |
| |
| private static final String CALIBRATION_FILE = "/persist/sns.reg"; |
| private static final String CMD = "senread"; |
| private static final String RESULT_PREFIX = "[RESULT]"; |
| private static final int DEFAULT_OFFSET = 0x0001; |
| protected static final int OFFSET_FAR = 30; |
| protected static final int OFFSET_NEAR = 30; |
| protected static final int BLOCK_LIMIT = 235; |
| protected static final int UNBLOCK_LIMIT = 180; |
| private static final int SEEK_NEAR = 0x00000100 + 4; |
| private static final int SEEK_FAR = 0x00000100 + 6; |
| private static final int SEEK_OFFSET = 0x00000020 + 8; |
| |
| private static final int STATE_START = 0; |
| private static final int STATE_BLOCK = 11; |
| private static final int STATE_BLOCK_READ = 12; |
| private static final int STATE_BLOCK_WARN = 13; |
| private static final int STATE_UNBLOCK = 21; |
| private static final int STATE_UNBLOCK_READ = 22; |
| private static final int STATE_UNBLOCK_WARN = 23; |
| private static final int STATE_CAL = 3; |
| private static final int STATE_SUCCESS = 4; |
| private static final int STATE_FAIL = 5; |
| private static final int STATE_FAIL_STEP_2 = 6; |
| |
| protected static final int DELAY = 1000; |
| |
| private Handler mHandler; |
| private TextView mStep1; |
| private TextView mText1; |
| private Button mButton1; |
| private TextView mStep2; |
| private TextView mText2; |
| private Button mButton2; |
| private TextView mStep3; |
| private TextView mText3; |
| private Button mButton3; |
| |
| private int mPersistedDataFar; |
| private int mPersistedDataNear; |
| private int mPersistedDataOffset; |
| private int mDataFar; |
| private int mDataNear; |
| private int mDataOffset; |
| private int mState = STATE_START; |
| |
| private ViewFlipper mFlipper; |
| private View mViewStep1; |
| private View mViewStep2; |
| private View mViewStep3; |
| private ProgressBar mProgressBar1; |
| private ProgressBar mProgressBar2; |
| |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| getPersistedValues(); |
| |
| mHandler=new Handler(); |
| |
| setContentView(R.layout.activity_calibration); |
| |
| mFlipper = (ViewFlipper) findViewById(R.id.viewFlipper); |
| |
| mFlipper.setInAnimation(this, R.anim.slide_in_from_left); |
| mFlipper.setOutAnimation(this, R.anim.slide_out_to_right); |
| |
| |
| LayoutInflater inflater = LayoutInflater.from(this); |
| mViewStep1 = inflater.inflate(R.layout.view_calibration_step,null); |
| mViewStep2 = inflater.inflate(R.layout.view_calibration_step,null); |
| mViewStep3 = inflater.inflate(R.layout.view_calibration_step,null); |
| |
| mFlipper.addView(mViewStep1); |
| mFlipper.addView(mViewStep2); |
| mFlipper.addView(mViewStep3); |
| |
| |
| mStep1 = (TextView) mViewStep1.findViewById(R.id.textview_heading); |
| mText1 = (TextView) mViewStep1.findViewById(R.id.maintext); |
| mButton1 = (Button) mViewStep1.findViewById(R.id.button); |
| mProgressBar1 = (ProgressBar) mViewStep1.findViewById(R.id.progressBar); |
| mProgressBar1.setVisibility(View.INVISIBLE); |
| |
| mButton1.setOnClickListener(new View.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| changeState(STATE_BLOCK_READ); |
| } |
| }); |
| mStep2 = (TextView) mViewStep2.findViewById(R.id.textview_heading); |
| mText2 = (TextView) mViewStep2.findViewById(R.id.maintext); |
| mButton2 = (Button) mViewStep2.findViewById(R.id.button); |
| mStep2.setText(getText(R.string.step_2)); |
| mText2.setText(getText(R.string.msg_unblock)); |
| mProgressBar2 = (ProgressBar) mViewStep2.findViewById(R.id.progressBar); |
| mProgressBar1.setVisibility(View.INVISIBLE); |
| |
| mButton2.setOnClickListener(new View.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| changeState(STATE_UNBLOCK_READ); |
| } |
| }); |
| mStep3 = (TextView) mViewStep3.findViewById(R.id.textview_heading); |
| mText3 = (TextView) mViewStep3.findViewById(R.id.maintext); |
| mButton3 = (Button) mViewStep3.findViewById(R.id.button); |
| mStep3.setText(getText(R.string.step_3)); |
| mText3.setText(getText(R.string.msg_calibration_success)); |
| mButton3.setText(R.string.reboot); |
| |
| mButton3.setOnClickListener(new View.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| mFlipper.showNext(); |
| PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); |
| powerManager.reboot(null); } |
| }); |
| } |
| |
| private void changeState(int state) { |
| mState = state; |
| update(); |
| } |
| |
| private void update() { |
| switch (mState) { |
| case STATE_START: |
| changeState(STATE_BLOCK); |
| break; |
| case STATE_BLOCK: |
| updateToBlock(); |
| break; |
| case STATE_BLOCK_READ: |
| updateToBlockRead(); |
| break; |
| case STATE_BLOCK_WARN: |
| case STATE_UNBLOCK: |
| updateToUnblock(); |
| break; |
| case STATE_UNBLOCK_READ: |
| updateToUnblockRead(); |
| break; |
| case STATE_UNBLOCK_WARN: |
| case STATE_CAL: |
| updateToCal(); |
| break; |
| case STATE_SUCCESS: |
| updateToSuccess(); |
| break; |
| case STATE_FAIL: |
| updateToFail(); |
| break; |
| case STATE_FAIL_STEP_2: |
| updateToFailStep2(); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| private void updateToBlock() { |
| //mText1.setText(getString(R.string.msg_block)); |
| mFlipper.setDisplayedChild(0); |
| |
| mStep1.setEnabled(true); |
| mText1.setEnabled(true); |
| mButton1.setEnabled(true); |
| mProgressBar1.setVisibility(View.INVISIBLE); |
| } |
| |
| private void updateToBlockRead() { |
| mText1.setText(getString(R.string.msg_reading)); |
| mButton1.setEnabled(false); |
| mProgressBar1.setVisibility(View.VISIBLE); |
| new Thread(new Runnable() { |
| @Override |
| public void run() { |
| final int value = read(); |
| |
| mHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| Log.d(TAG, "block value = " + String.format("%3d", value) + " (" + String.format("0x%04x", value) + ")"); |
| sleep(DELAY); |
| if (value >= BLOCK_LIMIT) { |
| mDataNear = value - OFFSET_NEAR; |
| changeState(STATE_UNBLOCK); |
| } else { |
| mText1.setText(getString(R.string.msg_fail_block)); |
| changeState(STATE_FAIL); |
| } |
| } |
| }); |
| } |
| }).start(); |
| } |
| |
| private void updateToCal() { |
| mText2.setText(R.string.msg_step_success); |
| mStep3.setEnabled(true); |
| mText3.setEnabled(true); |
| mText3.setText(getString(R.string.msg_cal)); |
| mHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| sleep(DELAY); |
| if (write()) { |
| mText3.setText(getString(R.string.msg_calibration_success)); |
| mButton3.setEnabled(true); |
| changeState(STATE_SUCCESS); |
| } else { |
| mText3.setText(getString(R.string.msg_fail_write_sns)); |
| changeState(STATE_FAIL); |
| } |
| } |
| }); |
| } |
| |
| private void updateToFail() { |
| mButton1.setEnabled(true); |
| changeState(STATE_BLOCK); |
| } |
| |
| private void updateToFailStep2() { |
| mText2.setText(getString(R.string.msg_fail_unlock)); |
| mButton2.setEnabled(true); |
| changeState(STATE_UNBLOCK); |
| } |
| |
| private void updateToSuccess() { |
| mFlipper.setDisplayedChild(2); |
| setSuccesfullyCalibrated(this, true); |
| mText2.setText(R.string.msg_step_success); |
| mButton3.setEnabled(true); |
| } |
| |
| private void updateToUnblockRead() { |
| mText2.setText(getString(R.string.msg_reading)); |
| mButton2.setEnabled(false); |
| mProgressBar2.setVisibility(View.VISIBLE); |
| new Thread(new Runnable() { |
| @Override |
| public void run() { |
| final int value = read(); |
| |
| mHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| Log.d(TAG, "unblock value = " + String.format("%3d", value) + " (" + String.format("0x%04x", value) + ")"); |
| sleep(DELAY); |
| if (value >= 0 && value < (mDataNear + OFFSET_NEAR - 5)) { |
| mDataFar = mDataNear - OFFSET_FAR; |
| mDataOffset = DEFAULT_OFFSET; |
| changeState(STATE_CAL); |
| } else { |
| mText1.setText(getString(R.string.msg_fail_unlock)); |
| changeState(STATE_FAIL); |
| } |
| } |
| }); |
| } |
| }).start(); |
| } |
| |
| @Override |
| protected void onPause() { |
| UpdateFinalizerService.startActionCheckCalibrationPending(this); |
| super.onPause(); |
| } |
| |
| private void updateToUnblock() { |
| mFlipper.setDisplayedChild(1); |
| mProgressBar2.setVisibility(View.INVISIBLE); |
| mStep2.setEnabled(true); |
| mText2.setEnabled(true); |
| mButton2.setEnabled(true); |
| } |
| |
| public static int read() { |
| final String line = exec(CMD); |
| int result = -1; |
| |
| if (line == null) { |
| // something went wrong with CMD, are we allowed to execute it? |
| Log.e(TAG, "Could not read sensor value"); |
| |
| // TODO display an error message |
| } else if (line.startsWith(RESULT_PREFIX)) { |
| try { |
| result = Integer.parseInt( line.replace(RESULT_PREFIX, "").trim() ); |
| } catch (Exception e) { |
| Log.wtf(TAG, e); |
| } |
| } |
| return result; |
| } |
| |
| private void getPersistedValues() { |
| byte[] buffer = new byte[4]; |
| try { |
| RandomAccessFile file = new RandomAccessFile(CALIBRATION_FILE, "r"); |
| |
| file.seek(SEEK_NEAR); |
| file.read(buffer, 0, 2); |
| mPersistedDataNear = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).getInt(); |
| Log.d(getString(R.string.logtag), "persisted near data = " + String.format("%3d", mPersistedDataNear) + " (" + String.format("0x%02x%02x", buffer[1], buffer[0]) + ")"); |
| |
| file.seek(SEEK_FAR); |
| file.read(buffer, 0, 2); |
| mPersistedDataFar = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).getInt(); |
| Log.d(getString(R.string.logtag), "persisted far data = " + String.format("%3d", mPersistedDataFar) + " (" + String.format("0x%02x%02x", buffer[1], buffer[0]) + ")"); |
| |
| file.seek(SEEK_OFFSET); |
| file.read(buffer, 0, 2); |
| mPersistedDataOffset = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).getInt(); |
| Log.d(getString(R.string.logtag), "persisted offset data = " + String.format("%3d", mPersistedDataOffset) + " (" + String.format("0x%02x%02x", buffer[1], buffer[0]) + ")"); |
| |
| file.close(); |
| } catch (Exception e) { |
| Log.wtf(TAG, e); |
| } |
| } |
| |
| |
| private boolean write() { |
| byte[] far = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(mDataFar).array(); |
| byte[] near = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(mDataNear).array(); |
| byte[] offset = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(mDataOffset).array(); |
| |
| Log.d(getString(R.string.logtag), "near data = " + String.format("%3d", mDataNear) + " (" + String.format("0x%02x%02x", near[1], near[0]) + ")"); |
| Log.d(getString(R.string.logtag), "far data = " + String.format("%3d", mDataFar) + " (" + String.format("0x%02x%02x", far[1], far[0]) + ")"); |
| Log.d(getString(R.string.logtag), "offset data = " + String.format("%3d", mDataOffset) + " (" + String.format("0x%02x%02x", offset[1], offset[0]) + ")"); |
| |
| try { |
| RandomAccessFile file = new RandomAccessFile(CALIBRATION_FILE, "rw"); |
| file.seek(SEEK_NEAR); |
| file.writeByte(near[0]); |
| file.writeByte(near[1]); |
| file.seek(SEEK_FAR); |
| file.writeByte(far[0]); |
| file.writeByte(far[1]); |
| file.seek(SEEK_OFFSET); |
| file.writeByte(offset[0]); |
| file.writeByte(offset[1]); |
| file.close(); |
| storeCalibrationData(); |
| return true; |
| } catch (Exception e) { |
| Log.wtf(TAG, e); |
| } |
| return false; |
| } |
| |
| private void storeCalibrationData() { |
| CalibrationDbHelper mDbHelper = new CalibrationDbHelper(this); |
| |
| // Gets the data repository in write mode |
| SQLiteDatabase db = mDbHelper.getWritableDatabase(); |
| |
| // Create a new map of values, where column names are the keys |
| ContentValues values = new ContentValues(); |
| values.put(CalibrationData.COLUMN_NAME_PREVIOUS_NEAR, mPersistedDataNear); |
| values.put(CalibrationData.COLUMN_NAME_PREVIOUS_FAR, mPersistedDataFar); |
| values.put(CalibrationData.COLUMN_NAME_PREVIOUS_OFFSET, mPersistedDataOffset); |
| values.put(CalibrationData.COLUMN_NAME_NEAR, mDataNear); |
| values.put(CalibrationData.COLUMN_NAME_FAR, mDataFar); |
| values.put(CalibrationData.COLUMN_NAME_OFFSET, mDataOffset); |
| |
| PackageInfo pInfo = null; |
| try { |
| pInfo = getPackageManager().getPackageInfo(getPackageName(), 0); |
| } catch (PackageManager.NameNotFoundException e) { |
| e.printStackTrace(); |
| } |
| int verCode = pInfo.versionCode; |
| values.put(CalibrationData.COLUMN_NAME_APP_VERSION, verCode); |
| |
| // Insert the new row, returning the primary key value of the new row |
| long newRowId; |
| newRowId = db.insert( |
| CalibrationData.TABLE_NAME, |
| null, |
| values); |
| |
| } |
| |
| private static String exec(String cmd) { |
| try { |
| Process proc = Runtime.getRuntime().exec(new String[]{cmd}); |
| BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream())); |
| return reader.readLine(); |
| } catch (IOException e) { |
| Log.wtf(TAG, "Could not execute command `" + cmd + "`", e); |
| return null; |
| } |
| } |
| |
| private static void sleep(int time) { |
| try { |
| Thread.sleep(time); |
| } catch (Exception e) { |
| |
| } |
| } |
| |
| protected static void setSuccesfullyCalibrated(Context ctx, boolean isSuccessfullyCalibrated) { |
| SharedPreferences sharedPref = ctx.getSharedPreferences( |
| ctx.getString(R.string.preference_file_key), MODE_PRIVATE); |
| SharedPreferences.Editor editor = sharedPref.edit(); |
| editor.putBoolean(ctx.getString(R.string.preference_successfully_calibrated),isSuccessfullyCalibrated); |
| editor.apply(); |
| } |
| |
| protected static boolean hasToBeCalibrated(Context ctx) { |
| SharedPreferences sharedPref = ctx.getSharedPreferences( |
| ctx.getString(R.string.preference_file_key), MODE_PRIVATE); |
| boolean wasCalibrated = sharedPref.getBoolean(ctx.getString(R.string.preference_successfully_calibrated),false); |
| boolean wasCalibratedEarlier = false; |
| try { |
| RandomAccessFile file = new RandomAccessFile(CALIBRATION_FILE, "rw"); |
| file.seek(SEEK_NEAR); |
| file.seek(SEEK_OFFSET); |
| byte offset0 = file.readByte(); |
| byte offset1 = file.readByte(); |
| file.close(); |
| /* offset is only 0 on devices that have not been calibrated. */ |
| wasCalibratedEarlier = (offset0 != 0 || offset1 != 0); |
| } catch (Exception e) { |
| Log.wtf(TAG, e); |
| } |
| return !wasCalibrated; |
| } |
| |
| } |