Thursday, April 20, 2017

Android Sensor - Tutorial


Android Sensors 

              Sensors measure a particular kind of physical quantity, such as force acting on device, light falling on a surface, or the temperature in a room.

               The majority of the sensors are Micro Electro Mechanical Sensors (MEMS), which are made on a tiny scale (in micrometers), usually on a silicon chip, with mechanical and electrical elements integrated together. The basic working principle behind MEMS is to measure the change in electric signal originating due to mechanical motion. This change in electric signals is converted to digital values by electric circuits. The accelerometer and gyroscope are the main examples of MEMS


Types of sensor values

Sensor values can be broadly divided into the following three categories:
  • Raw: These values are directly given by the sensor. The operating system simply passes these values to the apps without adding any correction logic. Accelerometers, proximity sensors, light sensors, and barometers are sensors that give raw values.

  • Calibrated: These values are computed by the operating system by adding extra correction algorithms, such as drift compensation and removing bias and noise over the raw values given by sensors. Step detector, step counter, and significant motion are sensors that give calibrated values by using an accelerometer as their base sensor. The magnetometer and gyroscope are special kinds of sensor that give both raw and calibrated values.

  • Fused: These values are derived from a combination of two or more sensors. Generally, these values are calculated by leveraging the strength of one sensor to accommodate the weaknesses of other sensors. Gravity and linear acceleration give fused values by using the accelerometer and gyroscope.

Types of sensor

Sensor
Value
Underlying Sensors
Description
Common Usage
Accelerometer
Raw
Accelerometer
This measures the acceleration force along the xy, and z axes (including gravity). Unit: m/s2
It can be used to detect motion such as shakes, swings, tilt, and physical forces applied on the phone.
Gravity
Fused
Accelerometer, Gyroscope
This measures the force of gravity along the xy, and zaxes. Unit: m/s2
It can be used to detect when the phone is in free fall.
Linear Acceleration
Fused
Accelerometer, Gyroscope
It measures the acceleration force along the xy, and z axes (excluding gravity). Unit: m/s2
It can be used to detect motion such as shakes, swings, tilt, and physical forces applied on phone.
Gyroscope
Raw, Calibrated
Gyroscope
This measures the rate of rotation of the device along the xy, and zaxes. Unit: rad/s
It can be used to detect rotation motions such as spin, turn, and any angular movement of the phone.
Step Detector
Calibrated
Accelerometer
This detects walking steps.
It can be used to detect when a user starts walking.
Step Counter
Calibrated
Accelerometer
It measures the number of steps taken by the user since the last reboot while the sensor was activated
It keeps track of the steps taken by the user per day.
Significant Motion
Calibrated
Accelerometer
It detects when there is significant motion on the phone because of walking, running, or driving.
It detects a significant motion event.
Rotation Vector
Fused
Accelerometer, Gyroscope, Magnetometer
This measures the rotation vector component along the xaxis (x * sin(θ/2)), y axis (y * sin(θ/2)), and z axis (z * sin(θ/2)). Scalar component of the rotation vector ((cos(θ/2)). Unitless.
It can be used in 3D games based on phone direction.

Android Sensor Stack




Components of the sensor framework

                   Android has provided methods, classes, and interfaces for accessing sensors and their data that is available on an Android device. These sets of methods, classes, and interfaces are collectively referred to as the sensor framework and are a part of the android.hardwarepackage. It consists of four major components: SensorManagerSensorSensorEvent, and SensorEventListener. The entry point to the framework is the SensorManagerclass, which allows an app to request sensor information and register to receive sensor data. When registered, sensor data values are sent to a SensorEventListener interface in the form of a SensorEvent class that contains information produced from a given sensor.


SensorManager

         SensorManager is the class that makes it possible for your app to get access to the sensors. It creates the instance of the system sensor service, which provides various APIs to access sensor information on the device. It exposes the methods that list the available and default sensors on the device. This class also provides several sensor constants that are used to report sensor accuracy, sampling period, and calibrate sensors. One of the important tasks of this class is to register and unregister sensor event listeners for accessing a particular sensor.

SensorEventListener
        SensorEventListener is the interface that provides two callbacks to receive the sensor notification (sensor event). OnSensorChanged() is the first method of the interface, which is called whenever there is any change in the sensor values. The change in sensor value is communicated through the SensorEvent object, passed as a parameter to this method. OnAccuracyChanged() is the second method, which is called whenever there is a change in the accuracy of sensor values. The sensor object and newly reported accuracy in integers are sent as parameters to this method. There are four accuracy integer constants supported by SensorManager. They are as follows:
  • SENSOR_STATUS_ACCURACY_HIGH
  • SENSOR_STATUS_ACCURACY_MEDIUM
  • SENSOR_STATUS_ACCURACY_LOW
  • SENSOR_STATUS_ACCURACY_UNRELIABLE

Sensor
        Sensor is the class that is used to create an instance of a specific sensor. This class provides various methods that let you determine a sensor's capabilities:
  • Maximum Range
  • Minimum Delay
  • Name
  • Power
  • Resolution
  • Reporting Mode
  • Type
  • Vendor
  • Version
  • isWakeUp Sensor

SensorEvent
        SensorEvent is a special kind of class that is used by the operating system to report changes in the sensor values to the listeners. This SensorEvent object contains the following four elements:
  • values[]: This is a multidimensional array that holds the sensor values
  • timestamp: This refers to the time in nanoseconds at which the event happened
  • accuracy: This is one of the four accuracy integer constants
  • sensor: This is the sensor type that generated this data


Checking the availability of the sensor at runtime

private SensorManager mSensorManager;
...
mSensorManager=
(SensorManager)getSystemService(Context.SENSOR_SERVICE);
if(mSensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE)!=null){
  // Success! There's a pressure sensor.
}else{
  // Failure! No pressure sensor.
}

Example
       import android.app.Activity;
       import android.content.Context;
       import android.hardware.Sensor;
       import android.hardware.SensorEvent;
       import android.hardware.SensorEventListener;
       import android.hardware.SensorManager;
       import android.os.Bundle;

       public class SensorActivity extends Activity implements
       SensorEventListener{
       private SensorManager mSensorManager;
       private Sensor mSensor;

       @Override
       protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
    
       mSensorManager =
       (SensorManager)this.getSystemService
       (Context.SENSOR_SERVICE );

       if(mSensorManager.getDefaultSensor
       (Sensor.TYPE_GYROSCOPE)!= null){
       mSensor =
       mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
       }
     }
@Override
       protected void onResume() {
         super.onResume();
         mSensorManager.registerListener(this, mSensor,
         SensorManager.SENSOR_DELAY_NORMAL);
       }

       @Override
       protected void onPause() {
         super.onPause();
         mSensorManager.unregisterListener(this);
       }

       @Override
       protected void onDestroy() {
         super.onDestroy();
         mSensorManager = null;
         mSensor = null;
       }
@Override
        public void onSensorChanged(SensorEvent event) {
            //event.values[] (do something with sensor values)
           //event.timestamp (do something with timestamp)
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int
        accuracy)
        {
            //Do something with changed accuracy
           //This method is mandatory to defined
        }

Listing the available sensors on a device


public class SensorListActivity extends Activity
       implements OnItemClickListener{

         private SensorManager mSensorManager;
         private ListView mSensorListView;
         private ListAdapter mListAdapter;
         private List<Sensor> mSensorsList;
@Override
     protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       mSensorManager =
       (SensorManager)this.getSystemService
       (Context.SENSOR_SERVICE);

       mSensorsList =
       mSensorManager.getSensorList(Sensor.TYPE_ALL);
       mSensorListView =
       (ListView)findViewById(R.id.session_list);
       mListAdapter = new ListAdapter();
       mSensorListView.setAdapter(mListAdapter);
       mSensorListView.setOnItemClickListener(this);  
     }
     private class ListAdapter extends BaseAdapter{ 

      private TextView mSensorName;

   
      @Override
      public int getCount() {
        return mSensorsList.size();
      }

      @Override
      public Object getItem(int position) {
        return mSensorsList.get(position).getName();
      }

      @Override
      public long getItemId(int position) {
        return position;
      }

      @Override
      public View getView(int position, View convertView,
      ViewGroup parent) {

      if(convertView==null){
        convertView = 
        getLayoutInflater().inflate(R.layout.list_rows,
        parent, false);
      }

      mSensorName =
      (TextView)convertView.findViewById(R.id.sensor_name);
      mSensorName.setText(mSensorsList.get(position)
      .getName());
      return convertView;
      }
   }
@Override
     public void onItemClick(AdapterView<?> parent, View view,
     int position,long id) {
    
       Intent intent = new Intent(getApplicationContext(),
       SensorCapabilityActivity.class);
       intent.putExtra(getResources()
       .getResourceName(R.string.sensor_type),
       mSensorsList.get(position).getType());
  
       startActivity(intent);
     }

Wake locks, wakeup sensors, and the FIFO queue

Wake lock can be obtained using the PowerManager object, which is provided by the system power service. The newWakeLock() method of PowerManager provides the object of wake lock. This newWakeLock() method accepts the type of wake lock and string tag for identification purposes.
PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"myLock");
mWakeLock.acquire();
 //Do some important work in background.
 mWakeLock.release();

Using the fingerprint sensor

          In order to support the fingerprint sensor, the Android platform has introduced a new system service, which is called the Finger Print Service, and it can be accessed using the instance of FingerprintManager.
Fingerprint sensor APIs require install time permission in the AndroidManifest.xmlfile (android.permission.USE_FINGERPRINT) and also runtime permission before using them.

public class FingerPrintActivity extends Activity { 

          private static final int FINGERPRINT_PERMISSION_REQUEST_CODE = 0;
          private FingerprintManager mFingerprintManager;
          //Alias for our key in the Android Key Store
          private static final String KEY_NAME = "my_key";
          private KeyStore mKeyStore;
          private KeyGenerator mKeyGenerator;
          private Cipher mCipher;
          private CancellationSignal mCancellationSignal;
          private Dialog mFingerPrintDialog;

          @Override
          protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.fingerprint_layout);
            mFingerprintManager = (FingerprintManager)getSystemService
            (FINGERPRINT_SERVICE);
            //As soon as Activity starts, check for the finger print
            conditions
            checkFingerPrintConditions()
          }

          public void initiateFingerPrintSensor(View v) {
            //Called from Layout button
            checkFingerPrintConditions();
          }


public void checkFingerPrintConditions() {

          if(mFingerprintManager.isHardwareDetected()) {
            if(mFingerprintManager.hasEnrolledFingerprints()) {
              if(ContextCompat.checkSelfPermission(this,
              Manifest.permission.USE_FINGERPRINT)!=
              PackageManager.PERMISSION_GRANTED) {
                //Requesting runtime finger print permission
                requestPermissions(new String[]
                {Manifest.permission.USE_FINGERPRINT},
                FINGERPRINT_PERMISSION_REQUEST_CODE);
              } else {
                //After all 3 conditions are met, then show FingerPrint
                Dialog
                showFingerPrintDialog();
              }
            } else {
              showAlertDialog("Finger Print Not Registered!", "Go to
              'Settings -> Security -> Fingerprint' and register at least
              one fingerprint");
            }
          } else {
            showAlertDialog("Finger Print Sensor Not Found!", "Finger Print
            Sensor could not be found on your phone.");
          }
        }

        @Override
        public void onRequestPermissionsResult(int requestCode, String[]
        permissions, int[] state) {

          //show FingerPrint Dialog, when runtime permission is granted
          if (requestCode == FINGERPRINT_PERMISSION_REQUEST_CODE
          && state[0] == PackageManager.PERMISSION_GRANTED) {

            showFingerPrintDialog();
          }
        }

        public void showAlertDialog(String title, String message){
          new android.app.AlertDialog.Builder(this).setTitle(title)    
          .setMessage(message).setIcon(android.R.drawable.ic_dialog_alert)
          .setPositiveButton("Cancel", new DialogInterface
          .OnClickListener()               
          {
             public void onClick(DialogInterface dialog, int whichButton) 
             {
                dialog.dismiss();
             }})
            .show();
          }


public void showFingerPrintDialog() {
          //First Initialize the FingerPrint Settings
          if(initFingerPrintSettings())
          {
            //Init Custom FingerPrint Dialog from xml
            mFingerPrintDialog = new Dialog(this);
            View view = LayoutInflater.from(this).inflate
            (R.layout.fingerpring_dialog, null, false);
            mFingerPrintDialog.setContentView(view);
            Button cancel = (Button) view.findViewById(R.id.cancelbutton);
            cancel.setOnClickListener(new View.OnClickListener() {
              @Override
              public void onClick(View arg0) {
                mCancellationSignal.cancel();
                mFingerPrintDialog.dismiss();
              }
            });

            //Stops the cancelling of the fingerprint dialog
            //by back press or touching accidentally on screen
            mFingerPrintDialog.setCanceledOnTouchOutside(false);
            mFingerPrintDialog.setCancelable(false);
            mFingerPrintDialog.show();
          }
          else
          {
            showAlertDialog("Error!", "Error in initiating Finger Print
            Cipher or Key!");
          }
        }

        public boolean initFingerPrintSettings() {

          //CancellationSignal requests authenticate api to stop scanning 
          mCancellationSignal = new CancellationSignal();
          if(initKey() && initCipher()) {
            mFingerprintManager.authenticate(new
            FingerprintManager.CryptoObject(mCipher),
            mCancellationSignal, 0, new AuthenticationListener(), null);
            return true;
          } else {
            return false;
          }
        }

public boolean initKey() {
          try {
            mKeyStore = KeyStore.getInstance("AndroidKeyStore");
            mKeyStore.load(null);
            mKeyGenerator = KeyGenerator.getInstance
            (KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
            mKeyGenerator.init(new KeyGenParameterSpec.Builder(KEY_NAME,
            KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
            .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
            .setUserAuthenticationRequired(true)                
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
            .build());
            mKeyGenerator.generateKey();
            return true;
          } catch (Exception e) {
            return false;
          }
        }

        public boolean initCipher() {
          try {
            mKeyStore.load(null);
            SecretKey key = (SecretKey) mKeyStore.getKey(KEY_NAME, null); 
            mCipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES +
            "/" + KeyProperties.BLOCK_MODE_CBC + "/" +
            KeyProperties.ENCRYPTION_PADDING_PKCS7);
            mCipher.init(Cipher.ENCRYPT_MODE, key);
            return true;
          } catch (KeyStoreException | CertificateException |
          UnrecoverableKeyException | IOException |
          NoSuchAlgorithmException | InvalidKeyException |
          NoSuchPaddingException e) {
            return false;
          }
        }


  class AuthenticationListener extends
        FingerprintManager.AuthenticationCallback{

          @Override
          public void onAuthenticationError(int errMsgId, CharSequence
          errString) {

            Toast.makeText(getApplicationContext(), "Authentication
            Error!", Toast.LENGTH_LONG).show();
          }

          @Override
          public void onAuthenticationHelp(int helpMsgId, CharSequence
          helpString) {
          }

          @Override
          public void onAuthenticationFailed() {

            Toast.makeText(getApplicationContext(), "Authentication
            Failed!", Toast.LENGTH_LONG).show();
          }

          @Override
          public void onAuthenticationSucceeded
          (FingerprintManager.AuthenticationResult result) {
             Toast.makeText(getApplicationContext(), "Authentication
            Success!", Toast.LENGTH_LONG).show();
            mFingerPrintDialog.dismiss();
          }
        }

The Step Counter and Detector Sensors – The Pedometer App

    public class StepsCounterActivity extends Activity
        implements SensorEventListener{

        private SensorManager mSensorManager;
        private Sensor mSensor;
        private boolean isSensorPresent;
        private TextView mStepsSinceReboot;

        @Override
        protected void onCreate(Bundle savedInstanceState){
          super.onCreate(savedInstanceState);
          setContentView(R.layout.stepcounter_layout);
        
          mStepsSinceReboot = (TextView)findViewById
          (R.id.stepssincereboot);
        
          mSensorManager = (SensorManager)
          this.getSystemService(Context.SENSOR_SERVICE);
          if(mSensorManager.getDefaultSensor
          (Sensor.TYPE_STEP_COUNTER) != null) {
            mSensor = mSensorManager.getDefaultSensor
            (Sensor.TYPE_STEP_COUNTER);
            isSensorPresent = true;
          } else {
            isSensorPresent = false;
          }
        }

        @Override
        protected void onResume() {
          super.onResume();
          if(isSensorPresent) {
            mSensorManager.registerListener(this, mSensor,
            SensorManager.SENSOR_DELAY_NORMAL);
          }
        }

        @Override
        protected void onPause() {
          super.onPause();
          if(isSensorPresent) {
            mSensorManager.unregisterListener(this);
          }
        }

        @Override
        public void onSensorChanged(SensorEvent event) {
          mStepsSinceReboot.setText("Steps since reboot:" +
          String.valueOf(event.values[0]));
        }
Share:

0 comments:

Post a Comment

Popular Posts

Contact Form

Name

Email *

Message *