Merge "DO NOT MERGE Disable AccelerometerListener when display is off" into ub-contactsdialer-a-dev
diff --git a/InCallUI/src/com/android/incallui/AccelerometerListener.java b/InCallUI/src/com/android/incallui/AccelerometerListener.java
index ca8e7d0..b5ad296 100644
--- a/InCallUI/src/com/android/incallui/AccelerometerListener.java
+++ b/InCallUI/src/com/android/incallui/AccelerometerListener.java
@@ -30,7 +30,7 @@
  * orientation of the phone. The client of this class is notified when
  * the orientation changes between horizontal and vertical.
  */
-public final class AccelerometerListener {
+public class AccelerometerListener {
     private static final String TAG = "AccelerometerListener";
     private static final boolean DEBUG = true;
     private static final boolean VDEBUG = false;
@@ -63,12 +63,15 @@
         public void orientationChanged(int orientation);
     }
 
-    public AccelerometerListener(Context context, OrientationListener listener) {
-        mListener = listener;
+    public AccelerometerListener(Context context) {
         mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
         mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
     }
 
+    public void setListener(OrientationListener listener) {
+        mListener = listener;
+    }
+
     public void enable(boolean enable) {
         if (DEBUG) Log.d(TAG, "enable(" + enable + ")");
         synchronized (this) {
@@ -155,7 +158,9 @@
                                 : (mOrientation == ORIENTATION_VERTICAL ? "vertical"
                                     : "unknown")));
                     }
-                    mListener.orientationChanged(mOrientation);
+                    if (mListener != null) {
+                        mListener.orientationChanged(mOrientation);
+                    }
                 }
                 break;
             }
diff --git a/InCallUI/src/com/android/incallui/InCallServiceImpl.java b/InCallUI/src/com/android/incallui/InCallServiceImpl.java
index e1a3411..b9a5aae 100644
--- a/InCallUI/src/com/android/incallui/InCallServiceImpl.java
+++ b/InCallUI/src/com/android/incallui/InCallServiceImpl.java
@@ -68,7 +68,10 @@
                 AudioModeProvider.getInstance(),
                 new StatusBarNotifier(context, contactInfoCache),
                 contactInfoCache,
-                new ProximitySensor(context, AudioModeProvider.getInstance())
+                new ProximitySensor(
+                        context,
+                        AudioModeProvider.getInstance(),
+                        new AccelerometerListener(context))
                 );
         InCallPresenter.getInstance().onServiceBind();
         InCallPresenter.getInstance().maybeStartRevealAnimation(intent);
diff --git a/InCallUI/src/com/android/incallui/ProximitySensor.java b/InCallUI/src/com/android/incallui/ProximitySensor.java
index 05a86d3..401ebd1 100644
--- a/InCallUI/src/com/android/incallui/ProximitySensor.java
+++ b/InCallUI/src/com/android/incallui/ProximitySensor.java
@@ -18,8 +18,11 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
 import android.os.PowerManager;
 import android.telecom.CallAudioState;
+import android.view.Display;
 
 import com.android.incallui.AudioModeProvider.AudioModeListener;
 import com.android.incallui.InCallPresenter.InCallState;
@@ -44,6 +47,7 @@
     private final PowerManager.WakeLock mProximityWakeLock;
     private final AudioModeProvider mAudioModeProvider;
     private final AccelerometerListener mAccelerometerListener;
+    private final ProximityDisplayListener mDisplayListener;
     private int mOrientation = AccelerometerListener.ORIENTATION_UNKNOWN;
     private boolean mUiShowing = false;
     private boolean mIsPhoneOffhook = false;
@@ -53,7 +57,8 @@
     // Gets updated whenever there is a Configuration change
     private boolean mIsHardKeyboardOpen;
 
-    public ProximitySensor(Context context, AudioModeProvider audioModeProvider) {
+    public ProximitySensor(Context context, AudioModeProvider audioModeProvider,
+            AccelerometerListener accelerometerListener) {
         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         if (mPowerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
             mProximityWakeLock = mPowerManager.newWakeLock(
@@ -62,7 +67,13 @@
             Log.w(TAG, "Device does not support proximity wake lock.");
             mProximityWakeLock = null;
         }
-        mAccelerometerListener = new AccelerometerListener(context, this);
+        mAccelerometerListener = accelerometerListener;
+        mAccelerometerListener.setListener(this);
+
+        mDisplayListener = new ProximityDisplayListener(
+                (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE));
+        mDisplayListener.register();
+
         mAudioModeProvider = audioModeProvider;
         mAudioModeProvider.addListener(this);
     }
@@ -71,6 +82,7 @@
         mAudioModeProvider.removeListener(this);
 
         mAccelerometerListener.enable(false);
+        mDisplayListener.unregister();
 
         turnOffProximitySensor(true);
     }
@@ -151,10 +163,16 @@
         updateProximitySensorMode();
     }
 
+    void onDisplayStateChanged(boolean isDisplayOn) {
+        Log.i(this, "isDisplayOn: " + isDisplayOn);
+        mAccelerometerListener.enable(isDisplayOn);
+    }
+
     /**
      * TODO: There is no way to determine if a screen is off due to proximity or if it is
      * legitimately off, but if ever we can do that in the future, it would be useful here.
      * Until then, this function will simply return true of the screen is off.
+     * TODO: Investigate whether this can be replaced with the ProximityDisplayListener.
      */
     public boolean isScreenReallyOff() {
         return !mPowerManager.isScreenOn();
@@ -251,4 +269,49 @@
                 turnOffProximitySensor(screenOnImmediately);
             }
         }
+
+    /**
+     * Implementation of a {@link DisplayListener} that maintains a binary state:
+     * Screen on vs screen off. Used by the proximity sensor manager to decide whether or not
+     * it needs to listen to accelerometer events.
+     */
+    public class ProximityDisplayListener implements DisplayListener {
+        private DisplayManager mDisplayManager;
+        private boolean mIsDisplayOn = true;
+
+        ProximityDisplayListener(DisplayManager displayManager) {
+            mDisplayManager = displayManager;
+        }
+
+        void register() {
+            mDisplayManager.registerDisplayListener(this, null);
+        }
+
+        void unregister() {
+            mDisplayManager.unregisterDisplayListener(this);
+        }
+
+        @Override
+        public void onDisplayRemoved(int displayId) {
+        }
+
+        @Override
+        public void onDisplayChanged(int displayId) {
+            if (displayId == Display.DEFAULT_DISPLAY) {
+                final Display display = mDisplayManager.getDisplay(displayId);
+
+                final boolean isDisplayOn = display.getState() != Display.STATE_OFF;
+                // For call purposes, we assume that as long as the screen is not truly off, it is
+                // considered on, even if it is in an unknown or low power idle state.
+                if (isDisplayOn != mIsDisplayOn) {
+                    mIsDisplayOn = isDisplayOn;
+                    onDisplayStateChanged(mIsDisplayOn);
+                }
+            }
+        }
+
+        @Override
+        public void onDisplayAdded(int displayId) {
+        }
+    }
 }
diff --git a/InCallUI/tests/src/com/android/incallui/ProximitySensorTest.java b/InCallUI/tests/src/com/android/incallui/ProximitySensorTest.java
new file mode 100644
index 0000000..d7b2f64
--- /dev/null
+++ b/InCallUI/tests/src/com/android/incallui/ProximitySensorTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 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.incallui;
+
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.test.InstrumentationTestCase;
+
+import com.android.incallui.InCallPresenter.InCallState;
+
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+public class ProximitySensorTest extends InstrumentationTestCase {
+    @Mock private AccelerometerListener mAccelerometerListener;
+    private MockCallListWrapper mCallList;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        System.setProperty("dexmaker.dexcache",
+                getInstrumentation().getTargetContext().getCacheDir().getPath());
+        MockitoAnnotations.initMocks(this);
+        mCallList = new MockCallListWrapper();
+    }
+
+    public void testAccelerometerBehaviorOnDisplayChange() {
+        final ProximitySensor proximitySensor =
+                new ProximitySensor(
+                        getInstrumentation().getContext(),
+                        new AudioModeProvider(),
+                        mAccelerometerListener);
+        verify(mAccelerometerListener, never()).enable(anyBoolean());
+        proximitySensor.onStateChange(null, InCallState.OUTGOING, mCallList.getCallList());
+        verify(mAccelerometerListener).enable(true);
+        verify(mAccelerometerListener, never()).enable(false);
+
+        proximitySensor.onDisplayStateChanged(false);
+        verify(mAccelerometerListener).enable(true);
+        verify(mAccelerometerListener).enable(false);
+
+        proximitySensor.onDisplayStateChanged(true);
+        verify(mAccelerometerListener, times(2)).enable(true);
+        verify(mAccelerometerListener).enable(false);
+    }
+}