com.achep.acdisplay.services.media.MediaController2KitKat.java Source code

Java tutorial

Introduction

Here is the source code for com.achep.acdisplay.services.media.MediaController2KitKat.java

Source

/*
 * 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 android.support.annotation.NonNull;
import android.support.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();
    }

}