1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.incallui;
18 
19 import android.content.Context;
20 import android.hardware.Sensor;
21 import android.hardware.SensorEvent;
22 import android.hardware.SensorEventListener;
23 import android.hardware.SensorManager;
24 import android.os.Handler;
25 import android.os.Message;
26 import android.util.Log;
27 
28 /**
29  * This class is used to listen to the accelerometer to monitor the orientation of the phone. The
30  * client of this class is notified when the orientation changes between horizontal and vertical.
31  */
32 public class AccelerometerListener {
33 
34   // Device orientation
35   public static final int ORIENTATION_UNKNOWN = 0;
36   public static final int ORIENTATION_VERTICAL = 1;
37   public static final int ORIENTATION_HORIZONTAL = 2;
38   private static final String TAG = "AccelerometerListener";
39   private static final boolean DEBUG = true;
40   private static final boolean VDEBUG = false;
41   private static final int ORIENTATION_CHANGED = 1234;
42   private static final int VERTICAL_DEBOUNCE = 100;
43   private static final int HORIZONTAL_DEBOUNCE = 500;
44   private static final double VERTICAL_ANGLE = 50.0;
45   private SensorManager mSensorManager;
46   private Sensor mSensor;
47   // mOrientation is the orientation value most recently reported to the client.
48   private int mOrientation;
49   // mPendingOrientation is the latest orientation computed based on the sensor value.
50   // This is sent to the client after a rebounce delay, at which point it is copied to
51   // mOrientation.
52   private int mPendingOrientation;
53   private OrientationListener mListener;
54   Handler mHandler =
55       new Handler() {
56         @Override
57         public void handleMessage(Message msg) {
58           switch (msg.what) {
59             case ORIENTATION_CHANGED:
60               synchronized (this) {
61                 mOrientation = mPendingOrientation;
62                 if (DEBUG) {
63                   Log.d(
64                       TAG,
65                       "orientation: "
66                           + (mOrientation == ORIENTATION_HORIZONTAL
67                               ? "horizontal"
68                               : (mOrientation == ORIENTATION_VERTICAL ? "vertical" : "unknown")));
69                 }
70                 if (mListener != null) {
71                   mListener.orientationChanged(mOrientation);
72                 }
73               }
74               break;
75           }
76         }
77       };
78   SensorEventListener mSensorListener =
79       new SensorEventListener() {
80         @Override
81         public void onSensorChanged(SensorEvent event) {
82           onSensorEvent(event.values[0], event.values[1], event.values[2]);
83         }
84 
85         @Override
86         public void onAccuracyChanged(Sensor sensor, int accuracy) {
87           // ignore
88         }
89       };
90 
AccelerometerListener(Context context)91   public AccelerometerListener(Context context) {
92     mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
93     mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
94   }
95 
setListener(OrientationListener listener)96   public void setListener(OrientationListener listener) {
97     mListener = listener;
98   }
99 
enable(boolean enable)100   public void enable(boolean enable) {
101     if (DEBUG) {
102       Log.d(TAG, "enable(" + enable + ")");
103     }
104     synchronized (this) {
105       if (enable) {
106         mOrientation = ORIENTATION_UNKNOWN;
107         mPendingOrientation = ORIENTATION_UNKNOWN;
108         mSensorManager.registerListener(
109             mSensorListener, mSensor, SensorManager.SENSOR_DELAY_NORMAL);
110       } else {
111         mSensorManager.unregisterListener(mSensorListener);
112         mHandler.removeMessages(ORIENTATION_CHANGED);
113       }
114     }
115   }
116 
setOrientation(int orientation)117   private void setOrientation(int orientation) {
118     synchronized (this) {
119       if (mPendingOrientation == orientation) {
120         // Pending orientation has not changed, so do nothing.
121         return;
122       }
123 
124       // Cancel any pending messages.
125       // We will either start a new timer or cancel alltogether
126       // if the orientation has not changed.
127       mHandler.removeMessages(ORIENTATION_CHANGED);
128 
129       if (mOrientation != orientation) {
130         // Set timer to send an event if the orientation has changed since its
131         // previously reported value.
132         mPendingOrientation = orientation;
133         final Message m = mHandler.obtainMessage(ORIENTATION_CHANGED);
134         // set delay to our debounce timeout
135         int delay = (orientation == ORIENTATION_VERTICAL ? VERTICAL_DEBOUNCE : HORIZONTAL_DEBOUNCE);
136         mHandler.sendMessageDelayed(m, delay);
137       } else {
138         // no message is pending
139         mPendingOrientation = ORIENTATION_UNKNOWN;
140       }
141     }
142   }
143 
onSensorEvent(double x, double y, double z)144   private void onSensorEvent(double x, double y, double z) {
145     if (VDEBUG) {
146       Log.d(TAG, "onSensorEvent(" + x + ", " + y + ", " + z + ")");
147     }
148 
149     // If some values are exactly zero, then likely the sensor is not powered up yet.
150     // ignore these events to avoid false horizontal positives.
151     if (x == 0.0 || y == 0.0 || z == 0.0) {
152       return;
153     }
154 
155     // magnitude of the acceleration vector projected onto XY plane
156     final double xy = Math.hypot(x, y);
157     // compute the vertical angle
158     double angle = Math.atan2(xy, z);
159     // convert to degrees
160     angle = angle * 180.0 / Math.PI;
161     final int orientation =
162         (angle > VERTICAL_ANGLE ? ORIENTATION_VERTICAL : ORIENTATION_HORIZONTAL);
163     if (VDEBUG) {
164       Log.d(TAG, "angle: " + angle + " orientation: " + orientation);
165     }
166     setOrientation(orientation);
167   }
168 
169   public interface OrientationListener {
170 
orientationChanged(int orientation)171     void orientationChanged(int orientation);
172   }
173 }
174