Face up or face down?

Is the Android device lying face up or face down? Or neither?

I tried to keep the onSensorChanged method as fast as possible because it can be called many times per second. The positive Z axis points out of the screen of the device; see this diagram. When lying face up, the z value is positive because the force of gravity that the device experiences is identical to the force it would experience in the absence of gravity if it were accelerating in the direction of the positive Z axis. A falling object gains 9.81 meters per second of speed during each second that it falls. For an Android device lying face up, z should therefore be 9.81, but the hardware in my Azpen A727 tablet registered 9.96, and my Amazon Fire HD 6 jittered around 10.1.

Two applications: my cousin’s sailboat, the planetarium in Yonkers.

Source code in Face.zip

  1. MainActivity.java. The MainActivity object acts as the SensorEventListener.
  2. R.java. Creates the int variables
    1. R.layout.activity_main
    2. R.id.textView
    3. R.string.face_up
    4. R.string.face_down
    5. R.string.face_sideways
  3. activity_main.xml.
  4. strings.xml. Contains three string resources:
    1. face_up
    2. face_down
    3. face_sideways
  5. AndroidManifest.xml
  6. build.gradle (Module: app).

Create the project

Override the methods onResume and onPause of class MainActivity. (onResume and onPause were originally defined in class Activity. Then they were overridden by the onResume and onPause in class FragmentActivity, which is a superclass of our AppCompatActivity. We will now override them again.) Open MainActivity.java Click on the whitespace outside onCreate but inside the curly braces of class MainActivity. Pull down
Code → Override Methods…
and type onPause to search for onPause in the drop-down menu. Press ↓ if necessary. When you find onPause, press OK. Ocerride onResume the same way.

Insert the words implements SensorEventListener. Then pull down
Code → Implement Methods…
and select onSensorChanged and onAccuracyChanged.

Things to try

  1. Does the display jitter annoyingly near FACE SIDEWAYS? If so, fix it like this in onSensorChanged.
            if (Math.abs(z) < .5) {
                s = face_sideways;	//z is close to zero
            } else if (z > 0) {
                s = face_up;
            } else {
                s = face_down;
            }
    

  2. In onSensorChanged, display the value of z too. Round it to two digits to the right of the decimal point, and display it using a total of at least 6 characters (e.g., -10.12). To make all the digits the same width, add the attribute android:typeface="monospace" to the TextView element in activity_main.xml. To keep the width of the TextView constant, and therefore to keep the position of its left edge constant, change its android:layout_width to "250dp".
            textView.setText(s + "\nz = " + String.format("%6.2f", z));
    
    Note that z is 9.81 when lying face up, and decreases to zero as we stand the device up.

  3. [What arccosine is for.] Print the angle of the device in onSensorChanged: 0° when lying face up and 90° when standing vertically. As the device is raised from a prone position, z goes from 9.81 to 0. Therefore the fraction z/9.81 goes from 1 to 0. Think of these fractions as the length of the shadow cast by the device under an overhead sun: the numbers get smaller as the device become more vertical. The function arccos gives us the angle created by the fraction.
    	//The absolute value of a cosine can never be greater than 1.
            double cosine = z / SensorManager.GRAVITY_EARTH;
    	if (cosine > 1) {
    		cosine = 1;
    	} else if (cosine < -1) {
    		cosine = -1;
    	}
    
    	//Find the angle that has the given cosine.
            double radians = Math.acos(cosine);
            double degrees = Math.toDegrees(radians);
    
            textView.setText(s + "\nz = " + String.format("%6.2f", z) + "\n"
                + String.format("%3.0f", degrees) + "\u00B0"); //Unicode degree symbol