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.services.media;
  
  import android.annotation.TargetApi;
  import android.app.Activity;
  import android.content.ComponentName;
  import android.content.Context;
  import android.content.Intent;
  import android.content.ServiceConnection;
  import android.media.MediaMetadataEditor;
  import android.media.MediaMetadataRetriever;
  import android.media.RemoteControlClient;
  import android.media.RemoteController;
  import android.os.Build;
  import android.os.IBinder;
  import androidx.annotation.NonNull;
  import androidx.annotation.Nullable;
  import android.support.v4.media.session.PlaybackStateCompat;
  import android.util.Log;
  import android.util.SparseIntArray;
  import android.view.KeyEvent;
  
  import com.achep.acdisplay.App;
  import com.achep.acdisplay.services.MediaService;
  
  import java.lang.ref.WeakReference;
  
  /**
   * Created by Artem Chepurnoy on 26.10.2014.
   */
  @SuppressWarnings("deprecation")
  @TargetApi(Build.VERSION_CODES.KITKAT)
  class MediaController2KitKat extends MediaController2 {
  
      static WeakReference<SparseIntArray> sStateSparse = new WeakReference<>(null);
  
      @Nullable
      private MediaService mService;
      private boolean mBound = false;
  
      private final RemoteController.OnClientUpdateListener mRCClientUpdateListener =
              new RemoteController.OnClientUpdateListener() {
  
                  @Override
                  public void onClientChange(boolean clearing) {
                      if (clearing) {
                          clearMetadata();
                      }
                  }
  
                  @Override
                  public void onClientPlaybackStateUpdate(int state) {
                      if (mPlaybackState != state) {
                          updatePlaybackState(state);
                      }
                  }
  
                  @Override
                  public void onClientPlaybackStateUpdate(int state, long stateChangeTimeMs,
                                                          long currentPosMs, float speed) {
                      onClientPlaybackStateUpdate(state);
                  }
  
                  @Override
                  public void onClientTransportControlUpdate(int transportControlFlags) {
                      // TODO: Bring more music control.
                  }
  
                  @Override
                  public void onClientMetadataUpdate(RemoteController.MetadataEditor metadataEditor) {
                      updateMetadata(metadataEditor);
                  }
  
              };
  
      private final ServiceConnection mConnection = new ServiceConnection() {
  
          @Override
          public void onServiceConnected(ComponentName className, IBinder service) {
              MediaService.B binder = (MediaService.B) service;
              mService = binder.getService();
              mService.setRemoteControllerEnabled();
              mService.setClientUpdateListener(mRCClientUpdateListener);
  
              mBound = true;
          }
  
          @Override
          public void onServiceDisconnected(ComponentName name) {
              mBound = false;
          }
  
      };
  
      private final SparseIntArray mStateSparse;
  
      /**
       * {@inheritDoc}
       */
      protected MediaController2KitKat(@NonNull Activity activity) {
          super(activity);
  
          SparseIntArray cachedStateSparse = sStateSparse.get();
          if (cachedStateSparse == null) {
              mStateSparse = generatePlaybackCompatSparse();
              sStateSparse = new WeakReference<>(mStateSparse);
          } else {
              mStateSparse = cachedStateSparse;
          }
      }
  
      @NonNull
      static SparseIntArray generatePlaybackCompatSparse() {
          SparseIntArray sia = new SparseIntArray();
          sia.put(RemoteControlClient.PLAYSTATE_BUFFERING, PlaybackStateCompat.STATE_BUFFERING);
          sia.put(RemoteControlClient.PLAYSTATE_PLAYING, PlaybackStateCompat.STATE_PLAYING);
          sia.put(RemoteControlClient.PLAYSTATE_PAUSED, PlaybackStateCompat.STATE_PAUSED);
          sia.put(RemoteControlClient.PLAYSTATE_ERROR, PlaybackStateCompat.STATE_ERROR);
          sia.put(RemoteControlClient.PLAYSTATE_REWINDING, PlaybackStateCompat.STATE_REWINDING);
          sia.put(RemoteControlClient.PLAYSTATE_FAST_FORWARDING, PlaybackStateCompat.STATE_FAST_FORWARDING);
          sia.put(RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS, PlaybackStateCompat.STATE_SKIPPING_TO_NEXT);
          sia.put(RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS, PlaybackStateCompat.STATE_SKIPPING_TO_PREVIOUS);
          return sia;
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public void onStart(Object... objects) {
          super.onStart();
          Intent intent = new Intent(App.ACTION_BIND_MEDIA_CONTROL_SERVICE);
          mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public void onStop(Object... objects) {
          mMetadata.clear();
  
          if (mBound) {
              assert mService != null;
              mService.setClientUpdateListener(null);
              mService.setRemoteControllerDisabled();
              mService = null;
              mBound = false;
          }
  
          mContext.unbindService(mConnection);
          super.onStop();
      }
  
      /**
       * {@inheritDoc}
       */
      public void sendMediaAction(int action) {
          if (mService == null) {
              Log.w(TAG, "Sending a media action on stopped controller.");
              return;
          }
  
          int keyCode;
          switch (action) {
              case ACTION_PLAY_PAUSE:
                  keyCode = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE;
                  break;
              case ACTION_STOP:
                  keyCode = KeyEvent.KEYCODE_MEDIA_STOP;
                  break;
              case ACTION_SKIP_TO_NEXT:
                  keyCode = KeyEvent.KEYCODE_MEDIA_NEXT;
                  break;
              case ACTION_SKIP_TO_PREVIOUS:
                  keyCode = KeyEvent.KEYCODE_MEDIA_PREVIOUS;
                  break;
              default:
                  throw new IllegalArgumentException();
          }
  
          // TODO We should think about sending these up/down events accurately with touch up/down
          // on the buttons, but in the near term this will interfere with the long press behavior.
          RemoteController rc = mService.getRemoteController();
          rc.sendMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
          rc.sendMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode));
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public void seekTo(long position) {
          if (mService == null) {
              Log.w(TAG, "Seeking a media on stopped controller.");
              return;
          }
  
          RemoteController rc = mService.getRemoteController();
          rc.seekTo(position);
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public long getPlaybackBufferedPosition() {
          return -1;
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public long getPlaybackPosition() {
          if (mService == null) {
              Log.w(TAG, "Getting a playback position on stopped controller.");
              return -1;
          }
  
          RemoteController rc = mService.getRemoteController();
          return rc.getEstimatedMediaPosition();
      }
  
      /**
       * {@inheritDoc}
       */
      protected void updatePlaybackState(int playbackStateRcc) {
          super.updatePlaybackState(mStateSparse.get(playbackStateRcc));
      }
  
      /**
       * Clears {@link #mMetadata metadata}. Same as calling
       * {@link #updateMetadata(android.media.RemoteController.MetadataEditor)}
       * with {@code null} parameter.
       *
       * @see #updateMetadata(android.media.RemoteController.MetadataEditor)
       */
      private void clearMetadata() {
          updateMetadata(null);
      }
  
      /**
       * Updates {@link #mMetadata metadata} from given remote metadata class.
       * This also updates play state.
       *
       * @param data Object of metadata to update from, or {@code null} to clear local metadata.
       * @see #clearMetadata()
       */
      private void updateMetadata(@Nullable RemoteController.MetadataEditor data) {
          if (data == null) {
              if (mMetadata.isEmpty()) return;
              mMetadata.clear();
          } else {
              mMetadata.title = data.getString(MediaMetadataRetriever.METADATA_KEY_TITLE, null);
              mMetadata.artist = data.getString(MediaMetadataRetriever.METADATA_KEY_ARTIST, null);
              mMetadata.album = data.getString(MediaMetadataRetriever.METADATA_KEY_ALBUM, null);
              mMetadata.duration = data.getLong(MediaMetadataRetriever.METADATA_KEY_DURATION, -1);
              mMetadata.bitmap = data.getBitmap(MediaMetadataEditor.BITMAP_KEY_ARTWORK, null);
              mMetadata.generateSubtitle();
          }
  
          notifyOnMetadataChanged();
      }
  
  }
  