content: /*
   * Copyright (C) 2014 AChep@xda <artemchep@gmail.com>
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version 2
   * of the License, or (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   * MA  02110-1301, USA.
   */
  package com.achep.acdisplay.ui.fragments;
  
  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.Handler;
  import android.os.Message;
  
  import com.achep.base.ui.fragments.leakcanary.LeakWatchFragment;
  
  /**
   * Non-UI fragment that listens to the proximity sensor to let us know
   * when to turn screen off.
   * <p>
   * <b>Please note, that you must
   * {@link #setListener(PocketFragment.OnSleepRequestListener) initialize listener}
   * before attaching fragment!</b>
   * </p>
   *
   * @author Artem Chepurnoy
   * @see #setListener(PocketFragment.OnSleepRequestListener)
   */
  public class PocketFragment extends LeakWatchFragment implements SensorEventListener {
  
      public static final String TAG = "PocketFragment";
  
      private static final int SLEEP_DELAY = 1500; // 1.5 sec.
      private static final int MSG_SLEEP = 0;
  
      private SensorManager mSensorManager;
      private Sensor mProximitySensor;
  
      private boolean mProximityAvailable;
  
      private boolean mNear;
      private boolean mFirstChange;
      private float mMaximumRange;
  
      private Handler mHandler = new Handler() {
          @Override
          public void handleMessage(Message msg) {
              super.handleMessage(msg);
              switch (msg.what) {
                  case MSG_SLEEP:
                      if (mListener != null) {
                          mListener.onSleepRequest();
                      }
                      break;
              }
          }
      };
  
      private OnSleepRequestListener mListener;
  
      /**
       * Interface definition for a callback to be invoked
       * when device has been put to the pocket.
       */
      public interface OnSleepRequestListener {
  
          /**
           * Called when parent activity may go to sleep because we're
           * in pocket at this moment.
           */
          boolean onSleepRequest();
  
      }
  
      public static PocketFragment newInstance() {
          return new PocketFragment();
      }
  
      public PocketFragment setListener(OnSleepRequestListener listener) {
          mListener = listener;
          return this;
      }
  
      @Override
      public void onAttach(Activity activity) {
          super.onAttach(activity);
          mSensorManager = (SensorManager) activity.getSystemService(Context.SENSOR_SERVICE);
          mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
          mProximityAvailable = mProximitySensor != null;
  
          if (mProximityAvailable) {
              mMaximumRange = mProximitySensor.getMaximumRange();
          }
      }
  
      @Override
      public void onResume() {
          super.onResume();
          if (mProximityAvailable) {
              mSensorManager.registerListener(this, mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
              mFirstChange = true;
              mNear = false;
          }
      }
  
      @Override
      public void onPause() {
          super.onPause();
          mHandler.removeCallbacksAndMessages(null);
          mSensorManager.unregisterListener(this);
      }
  
      @Override
      public void onDetach() {
          super.onDetach();
          mSensorManager = null;
          mProximitySensor = null;
      }
  
      @Override
      public void onSensorChanged(SensorEvent event) {
          final float distance = event.values[0];
          final boolean isNear = distance < mMaximumRange || distance < 1.0f;
          final boolean changed = mNear != (mNear = isNear) || mFirstChange;
  
          if (!changed) {
              // Well just in cause if proximity sensor is NOT always eventual.
              // This should not happen, but who knows... I found maximum
              // range buggy enough.
              return;
          }
  
          mHandler.removeCallbacksAndMessages(null);
  
          if (!isNear) {
              return;
          }
  
          mHandler.sendEmptyMessageDelayed(MSG_SLEEP, SLEEP_DELAY);
      }
  
      @Override
      public void onAccuracyChanged(Sensor sensor, int i) { /* unused */ }
  }
  