Use new PowerManager API to implement voicemail proximity

This allows us to fully blank out the screen instead of faking
a black screen leaving the nav bar and notification bar active.

Bug: 11734938
Change-Id: I716642fa7f85776d1701d3b95736065dec4d2eac
diff --git a/res/layout/call_detail.xml b/res/layout/call_detail.xml
index 35c41c0..522c698 100644
--- a/res/layout/call_detail.xml
+++ b/res/layout/call_detail.xml
@@ -100,18 +100,4 @@
 
 
     </LinearLayout>
-    <!--
-         Used to hide the UI when playing a voicemail and the proximity sensor
-         is detecting something near the screen.
-      -->
-    <View
-        android:id="@+id/blank"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="@android:color/black"
-        android:visibility="gone"
-        android:clickable="true"
-        android:layout_alignParentStart="true"
-        android:layout_alignParentTop="true"
-        />
 </RelativeLayout>
diff --git a/src/com/android/dialer/CallDetailActivity.java b/src/com/android/dialer/CallDetailActivity.java
index fb663d7..08c9e02 100644
--- a/src/com/android/dialer/CallDetailActivity.java
+++ b/src/com/android/dialer/CallDetailActivity.java
@@ -27,6 +27,7 @@
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
+import android.os.PowerManager;
 import android.provider.CallLog;
 import android.provider.CallLog.Calls;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
@@ -83,11 +84,6 @@
     private static final char LEFT_TO_RIGHT_EMBEDDING = '\u202A';
     private static final char POP_DIRECTIONAL_FORMATTING = '\u202C';
 
-    /** The time to wait before enabling the blank the screen due to the proximity sensor. */
-    private static final long PROXIMITY_BLANK_DELAY_MILLIS = 100;
-    /** The time to wait before disabling the blank the screen due to the proximity sensor. */
-    private static final long PROXIMITY_UNBLANK_DELAY_MILLIS = 500;
-
     /** The enumeration of {@link AsyncTask} objects used in this class. */
     public enum Tasks {
         MARK_VOICEMAIL_READ,
@@ -144,59 +140,7 @@
     /** Whether we should show "remove from call log" in the options menu. */
     private boolean mHasRemoveFromCallLogOption;
 
-    private ProximitySensorManager mProximitySensorManager;
-    private final ProximitySensorListener mProximitySensorListener = new ProximitySensorListener();
-
-    /** Listener to changes in the proximity sensor state. */
-    private class ProximitySensorListener implements ProximitySensorManager.Listener {
-        /** Used to show a blank view and hide the action bar. */
-        private final Runnable mBlankRunnable = new Runnable() {
-            @Override
-            public void run() {
-                View blankView = findViewById(R.id.blank);
-                blankView.setVisibility(View.VISIBLE);
-                getActionBar().hide();
-            }
-        };
-        /** Used to remove the blank view and show the action bar. */
-        private final Runnable mUnblankRunnable = new Runnable() {
-            @Override
-            public void run() {
-                View blankView = findViewById(R.id.blank);
-                blankView.setVisibility(View.GONE);
-                getActionBar().show();
-            }
-        };
-
-        @Override
-        public synchronized void onNear() {
-            clearPendingRequests();
-            postDelayed(mBlankRunnable, PROXIMITY_BLANK_DELAY_MILLIS);
-        }
-
-        @Override
-        public synchronized void onFar() {
-            clearPendingRequests();
-            postDelayed(mUnblankRunnable, PROXIMITY_UNBLANK_DELAY_MILLIS);
-        }
-
-        /** Removed any delayed requests that may be pending. */
-        public synchronized void clearPendingRequests() {
-            View blankView = findViewById(R.id.blank);
-            blankView.removeCallbacks(mBlankRunnable);
-            blankView.removeCallbacks(mUnblankRunnable);
-        }
-
-        /** Post a {@link Runnable} with a delay on the main thread. */
-        private synchronized void postDelayed(Runnable runnable, long delayMillis) {
-            // Post these instead of executing immediately so that:
-            // - They are guaranteed to be executed on the main thread.
-            // - If the sensor values changes rapidly for some time, the UI will not be
-            //   updated immediately.
-            View blankView = findViewById(R.id.blank);
-            blankView.postDelayed(runnable, delayMillis);
-        }
-    }
+    private PowerManager.WakeLock mProximityWakeLock;
 
     static final String[] CALL_LOG_PROJECTION = new String[] {
         CallLog.Calls.DATE,
@@ -250,7 +194,14 @@
         mAccountLabel = (TextView) findViewById(R.id.phone_account_label);
         mDefaultCountryIso = GeoUtil.getCurrentCountryIso(this);
         mContactPhotoManager = ContactPhotoManager.getInstance(this);
-        mProximitySensorManager = new ProximitySensorManager(this, mProximitySensorListener);
+        final PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
+        if (powerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
+            mProximityWakeLock = powerManager.newWakeLock(
+                    PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG);
+        } else {
+            mProximityWakeLock = null;
+        }
+
         mContactInfoHelper = new ContactInfoHelper(this, GeoUtil.getCurrentCountryIso(this));
         getActionBar().setDisplayHomeAsUpEnabled(true);
 
@@ -751,18 +702,34 @@
     protected void onPause() {
         // Immediately stop the proximity sensor.
         disableProximitySensor(false);
-        mProximitySensorListener.clearPendingRequests();
         super.onPause();
     }
 
     @Override
     public void enableProximitySensor() {
-        mProximitySensorManager.enable();
+        if (mProximityWakeLock == null) {
+            return;
+        }
+        if (!mProximityWakeLock.isHeld()) {
+            Log.i(TAG, "Acquiring proximity wake lock");
+            mProximityWakeLock.acquire();
+        } else {
+            Log.i(TAG, "Proximity wake lock already acquired");
+        }
     }
 
     @Override
     public void disableProximitySensor(boolean waitForFarState) {
-        mProximitySensorManager.disable(waitForFarState);
+        if (mProximityWakeLock == null) {
+            return;
+        }
+        if (mProximityWakeLock.isHeld()) {
+            Log.i(TAG, "Releasing proximity wake lock");
+            int flags = (waitForFarState ? PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY : 0);
+            mProximityWakeLock.release(flags);
+        } else {
+            Log.i(TAG, "Proximity wake lock already released");
+        }
     }
 
     private void closeSystemDialogs() {
diff --git a/src/com/android/dialer/ProximitySensorManager.java b/src/com/android/dialer/ProximitySensorManager.java
deleted file mode 100644
index 42d740f..0000000
--- a/src/com/android/dialer/ProximitySensorManager.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Copyright (C) 2011 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.dialer;
-
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-
-import javax.annotation.concurrent.GuardedBy;
-
-/**
- * Manages the proximity sensor and notifies a listener when enabled.
- */
-public class ProximitySensorManager {
-    /**
-     * Listener of the state of the proximity sensor.
-     * <p>
-     * This interface abstracts two possible states for the proximity sensor, near and far.
-     * <p>
-     * The actual meaning of these states depends on the actual sensor.
-     */
-    public interface Listener {
-        /** Called when the proximity sensor transitions from the far to the near state. */
-        public void onNear();
-        /** Called when the proximity sensor transitions from the near to the far state. */
-        public void onFar();
-    }
-
-    public static enum State {
-        NEAR, FAR
-    }
-
-    private final ProximitySensorEventListener mProximitySensorListener;
-
-    /**
-     * The current state of the manager, i.e., whether it is currently tracking the state of the
-     * sensor.
-     */
-    private boolean mManagerEnabled;
-
-    /**
-     * The listener to the state of the sensor.
-     * <p>
-     * Contains most of the logic concerning tracking of the sensor.
-     * <p>
-     * After creating an instance of this object, one should call {@link #register()} and
-     * {@link #unregister()} to enable and disable the notifications.
-     * <p>
-     * Instead of calling unregister, one can call {@link #unregisterWhenFar()} to unregister the
-     * listener the next time the sensor reaches the {@link State#FAR} state if currently in the
-     * {@link State#NEAR} state.
-     */
-    private static class ProximitySensorEventListener implements SensorEventListener {
-        private static final float FAR_THRESHOLD = 5.0f;
-
-        private final SensorManager mSensorManager;
-        private final Sensor mProximitySensor;
-        private final float mMaxValue;
-        private final Listener mListener;
-
-        /**
-         * The last state of the sensor.
-         * <p>
-         * Before registering and after unregistering we are always in the {@link State#FAR} state.
-         */
-        @GuardedBy("this") private State mLastState;
-        /**
-         * If this flag is set to true, we are waiting to reach the {@link State#FAR} state and
-         * should notify the listener and unregister when that happens.
-         */
-        @GuardedBy("this") private boolean mWaitingForFarState;
-
-        public ProximitySensorEventListener(SensorManager sensorManager, Sensor proximitySensor,
-                Listener listener) {
-            mSensorManager = sensorManager;
-            mProximitySensor = proximitySensor;
-            mMaxValue = proximitySensor.getMaximumRange();
-            mListener = listener;
-            // Initialize at far state.
-            mLastState = State.FAR;
-            mWaitingForFarState = false;
-        }
-
-        @Override
-        public void onSensorChanged(SensorEvent event) {
-            // Make sure we have a valid value.
-            if (event.values == null) return;
-            if (event.values.length == 0) return;
-            float value = event.values[0];
-            // Convert the sensor into a NEAR/FAR state.
-            State state = getStateFromValue(value);
-            synchronized (this) {
-                // No change in state, do nothing.
-                if (state == mLastState) return;
-                // Keep track of the current state.
-                mLastState = state;
-                // If we are waiting to reach the far state and we are now in it, unregister.
-                if (mWaitingForFarState && mLastState == State.FAR) {
-                    unregisterWithoutNotification();
-                }
-            }
-            // Notify the listener of the state change.
-            switch (state) {
-                case NEAR:
-                    mListener.onNear();
-                    break;
-
-                case FAR:
-                    mListener.onFar();
-                    break;
-            }
-        }
-
-        @Override
-        public void onAccuracyChanged(Sensor sensor, int accuracy) {
-            // Nothing to do here.
-        }
-
-        /** Returns the state of the sensor given its current value. */
-        private State getStateFromValue(float value) {
-            // Determine if the current value corresponds to the NEAR or FAR state.
-            // Take case of the case where the proximity sensor is binary: if the current value is
-            // equal to the maximum, we are always in the FAR state.
-            return (value > FAR_THRESHOLD || value == mMaxValue) ? State.FAR : State.NEAR;
-        }
-
-        /**
-         * Unregister the next time the sensor reaches the {@link State#FAR} state.
-         */
-        public synchronized void unregisterWhenFar() {
-            if (mLastState == State.FAR) {
-                // We are already in the far state, just unregister now.
-                unregisterWithoutNotification();
-            } else {
-                mWaitingForFarState = true;
-            }
-        }
-
-        /** Register the listener and call the listener as necessary. */
-        public synchronized void register() {
-            // It is okay to register multiple times.
-            mSensorManager.registerListener(this, mProximitySensor, SensorManager.SENSOR_DELAY_UI);
-            // We should no longer be waiting for the far state if we are registering again.
-            mWaitingForFarState = false;
-        }
-
-        public void unregister() {
-            State lastState;
-            synchronized (this) {
-                unregisterWithoutNotification();
-                lastState = mLastState;
-                // Always go back to the FAR state. That way, when we register again we will get a
-                // transition when the sensor gets into the NEAR state.
-                mLastState = State.FAR;
-            }
-            // Notify the listener if we changed the state to FAR while unregistering.
-            if (lastState != State.FAR) {
-                mListener.onFar();
-            }
-        }
-
-        @GuardedBy("this")
-        private void unregisterWithoutNotification() {
-            mSensorManager.unregisterListener(this);
-            mWaitingForFarState = false;
-        }
-    }
-
-    public ProximitySensorManager(Context context, Listener listener) {
-        SensorManager sensorManager =
-                (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
-        Sensor proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
-        if (proximitySensor == null) {
-            // If there is no sensor, we should not do anything.
-            mProximitySensorListener = null;
-        } else {
-            mProximitySensorListener =
-                    new ProximitySensorEventListener(sensorManager, proximitySensor, listener);
-        }
-    }
-
-    /**
-     * Enables the proximity manager.
-     * <p>
-     * The listener will start getting notifications of events.
-     * <p>
-     * This method is idempotent.
-     */
-    public void enable() {
-        if (mProximitySensorListener != null && !mManagerEnabled) {
-            mProximitySensorListener.register();
-            mManagerEnabled = true;
-        }
-    }
-
-    /**
-     * Disables the proximity manager.
-     * <p>
-     * The listener will stop receiving notifications of events, possibly after receiving a last
-     * {@link Listener#onFar()} callback.
-     * <p>
-     * If {@code waitForFarState} is true, if the sensor is not currently in the {@link State#FAR}
-     * state, the listener will receive a {@link Listener#onFar()} callback the next time the sensor
-     * actually reaches the {@link State#FAR} state.
-     * <p>
-     * If {@code waitForFarState} is false, the listener will receive a {@link Listener#onFar()}
-     * callback immediately if the sensor is currently not in the {@link State#FAR} state.
-     * <p>
-     * This method is idempotent.
-     */
-    public void disable(boolean waitForFarState) {
-        if (mProximitySensorListener != null && mManagerEnabled) {
-            if (waitForFarState) {
-                mProximitySensorListener.unregisterWhenFar();
-            } else {
-                mProximitySensorListener.unregister();
-            }
-            mManagerEnabled = false;
-        }
-    }
-}