content: /*
   * Copyright (C) 2015 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.services.switches;
  
  import android.content.Context;
  import android.os.Handler;
  import androidx.annotation.NonNull;
  import android.telephony.PhoneStateListener;
  import android.telephony.TelephonyManager;
  
  import com.achep.acdisplay.services.Switch;
  
  import java.lang.ref.Reference;
  import java.lang.ref.WeakReference;
  
  /**
   * Prevents {@link com.achep.acdisplay.services.SwitchService} from working
   * while an phone app is calling.
   *
   * @author Artem Chepurnoy
   */
  public final class PhoneCallSwitch extends Switch {
  
      private static final String TAG = "PhoneCallSwitch";
  
      @NonNull
      private final CallMonitor mCallMonitor;
  
      public PhoneCallSwitch(@NonNull Context context, @NonNull Callback callback) {
          super(context, callback);
          mCallMonitor = new CallMonitor(context, new CallMonitor.OnCallStateChangedListener() {
              @Override
              public void onCallStateChanged(int state) {
                  if (isActive()) {
                      requestActive();
                  } else requestInactive();
              }
          });
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public void onCreate() {
          mCallMonitor.start();
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public void onDestroy() {
          mCallMonitor.stop();
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public boolean isActive() {
          return !mCallMonitor.isCalling();
      }
  
      /**
       * @return whether if the phone is calling now
       */
      public boolean isCalling() {
          return mCallMonitor.isCalling();
      }
  
      /**
       * @author Artem Chepurnoy
       */
      private static class CallMonitor {
  
          private boolean mStarted;
  
          /**
           * @author Artem Chepurnoy
           */
          public interface OnCallStateChangedListener {
  
              /**
               * Callback invoked when device call state changes.
               */
              void onCallStateChanged(int state);
  
          }
  
          @NonNull
          private final Context mContext;
          @NonNull
          private final Handler mHandler;
          @NonNull
          private final OnCallStateChangedListener mCallback;
  
          private Listener mListener;
          private TelephonyManager mTelephonyManager;
  
          public CallMonitor(
                  @NonNull Context context,
                  @NonNull OnCallStateChangedListener listener) {
              mContext = context;
              mCallback = listener;
              mHandler = new Handler();
              mListener = new Listener(this);
          }
  
          public void start() {
              mStarted = true;
              mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
              mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_CALL_STATE);
          }
  
          public void stop() {
              mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_NONE);
              mTelephonyManager = null;
              mStarted = false;
          }
  
          /**
           * @return the current call state.
           * @see #isCalling()
           */
          public int getCallState() {
              return mListener.getCallState();
          }
  
          /**
           * @return whether if the phone is calling now
           * @see #getCallState()
           */
          public boolean isCalling() {
              final int state = getCallState();
              return state == TelephonyManager.CALL_STATE_RINGING
                      || state == TelephonyManager.CALL_STATE_OFFHOOK;
          }
  
          private void notifyCallStateChanged(final int state) {
              mHandler.post(new Runnable() {
                  @Override
                  public void run() {
                      if (mStarted) mCallback.onCallStateChanged(state);
                  }
              });
          }
  
          /**
           * @author Artem Chepurnoy
           */
          private static class Listener extends PhoneStateListener {
  
              @NonNull
              private final Reference<CallMonitor> mCallHandlerRef;
  
              /**
               * The current call state.
               */
              private int mState;
  
              public Listener(@NonNull CallMonitor callStateListener) {
                  mCallHandlerRef = new WeakReference<>(callStateListener);
                  mState = TelephonyManager.CALL_STATE_IDLE;
              }
  
              @Override
              public void onCallStateChanged(int state, String incomingNumber) {
                  super.onCallStateChanged(state, incomingNumber);
                  if (mState == state) return;
                  mState = state;
                  // Notify the call handler
                  CallMonitor callStateListener = mCallHandlerRef.get();
                  if (callStateListener != null) callStateListener.notifyCallStateChanged(state);
              }
  
              /**
               * @return the current call state.
               */
              public int getCallState() {
                  return mState;
              }
  
          }
  
      }
  
  }
  