FileDocCategorySizeDatePackage
KeyguardTransportControlView.javaAPI DocAndroid 5.1 API26881Thu Mar 12 22:22:42 GMT 2015com.android.keyguard

KeyguardTransportControlView

public class KeyguardTransportControlView extends android.widget.FrameLayout
This is the widget responsible for showing music controls in keyguard.

Fields Summary
private static final int
RESET_TO_METADATA_DELAY
protected static final boolean
DEBUG
protected static final String
TAG
private static final boolean
ANIMATE_TRANSITIONS
protected static final long
QUIESCENT_PLAYBACK_FACTOR
private android.view.ViewGroup
mMetadataContainer
private android.view.ViewGroup
mInfoContainer
private android.widget.TextView
mTrackTitle
private android.widget.TextView
mTrackArtistAlbum
private android.view.View
mTransientSeek
private android.widget.SeekBar
mTransientSeekBar
private android.widget.TextView
mTransientSeekTimeElapsed
private android.widget.TextView
mTransientSeekTimeTotal
private android.widget.ImageView
mBtnPrev
private android.widget.ImageView
mBtnPlay
private android.widget.ImageView
mBtnNext
private Metadata
mMetadata
private int
mTransportControlFlags
private int
mCurrentPlayState
private android.media.AudioManager
mAudioManager
private android.media.RemoteController
mRemoteController
private android.widget.ImageView
mBadge
private boolean
mSeekEnabled
private DateFormat
mFormat
private Date
mTempDate
private RemoteController.MetadataEditor
mPopulateMetadataWhenAttached
The metadata which should be populated into the view once we've been attached
private RemoteController.OnClientUpdateListener
mRCClientUpdateListener
private final UpdateSeekBarRunnable
mUpdateSeekBars
private final Runnable
mResetToMetadata
private final OnClickListener
mTransportCommandListener
private final OnLongClickListener
mTransportShowSeekBarListener
private final FutureSeekRunnable
mFutureSeekRunnable
private final SeekBar.OnSeekBarChangeListener
mOnSeekBarChangeListener
private static final int
TRANSITION_DURATION
private final android.transition.TransitionSet
mMetadataChangeTransition
KeyguardHostView.TransportControlCallback
mTransportControlCallback
private final KeyguardUpdateMonitorCallback
mUpdateMonitor
Constructors Summary
public KeyguardTransportControlView(android.content.Context context, android.util.AttributeSet attrs)


         
        super(context, attrs);
        if (DEBUG) Log.v(TAG, "Create TCV " + this);
        mAudioManager = new AudioManager(mContext);
        mCurrentPlayState = RemoteControlClient.PLAYSTATE_NONE; // until we get a callback
        mRemoteController = new RemoteController(context, mRCClientUpdateListener);

        final DisplayMetrics dm = context.getResources().getDisplayMetrics();
        final int dim = Math.max(dm.widthPixels, dm.heightPixels);
        mRemoteController.setArtworkConfiguration(true, dim, dim);

        final ChangeText tc = new ChangeText();
        tc.setChangeBehavior(ChangeText.CHANGE_BEHAVIOR_OUT_IN);
        final TransitionSet inner = new TransitionSet();
        inner.addTransition(tc).addTransition(new ChangeBounds());
        final TransitionSet tg = new TransitionSet();
        tg.addTransition(new Fade(Fade.OUT)).addTransition(inner).
                addTransition(new Fade(Fade.IN));
        tg.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
        tg.setDuration(TRANSITION_DURATION);
        mMetadataChangeTransition = tg;
    
Methods Summary
voidcancelResetToMetadata()

        removeCallbacks(mResetToMetadata);
    
voidclearMetadata()

        mPopulateMetadataWhenAttached = null;
        mMetadata.clear();
        populateMetadata();
    
voiddelayResetToMetadata()

        removeCallbacks(mResetToMetadata);
        postDelayed(mResetToMetadata, RESET_TO_METADATA_DELAY);
    
public voidonAttachedToWindow()

        super.onAttachedToWindow();
        if (DEBUG) Log.v(TAG, "onAttachToWindow()");
        if (mPopulateMetadataWhenAttached != null) {
            updateMetadata(mPopulateMetadataWhenAttached);
            mPopulateMetadataWhenAttached = null;
        }
        if (DEBUG) Log.v(TAG, "Registering TCV " + this);
        mMetadata.clear();
        mAudioManager.registerRemoteController(mRemoteController);
        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitor);
    
protected voidonConfigurationChanged(android.content.res.Configuration newConfig)

        super.onConfigurationChanged(newConfig);
        final DisplayMetrics dm = getContext().getResources().getDisplayMetrics();
        final int dim = Math.max(dm.widthPixels, dm.heightPixels);
        mRemoteController.setArtworkConfiguration(true, dim, dim);
    
public voidonDetachedFromWindow()

        if (DEBUG) Log.v(TAG, "onDetachFromWindow()");
        super.onDetachedFromWindow();
        if (DEBUG) Log.v(TAG, "Unregistering TCV " + this);
        mAudioManager.unregisterRemoteController(mRemoteController);
        KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitor);
        mMetadata.clear();
        removeCallbacks(mUpdateSeekBars);
    
public voidonFinishInflate()

        super.onFinishInflate();
        mInfoContainer = (ViewGroup) findViewById(R.id.info_container);
        mMetadataContainer = (ViewGroup) findViewById(R.id.metadata_container);
        mBadge = (ImageView) findViewById(R.id.badge);
        mTrackTitle = (TextView) findViewById(R.id.title);
        mTrackArtistAlbum = (TextView) findViewById(R.id.artist_album);
        mTransientSeek = findViewById(R.id.transient_seek);
        mTransientSeekBar = (SeekBar) findViewById(R.id.transient_seek_bar);
        mTransientSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangeListener);
        mTransientSeekTimeElapsed = (TextView) findViewById(R.id.transient_seek_time_elapsed);
        mTransientSeekTimeTotal = (TextView) findViewById(R.id.transient_seek_time_remaining);
        mBtnPrev = (ImageView) findViewById(R.id.btn_prev);
        mBtnPlay = (ImageView) findViewById(R.id.btn_play);
        mBtnNext = (ImageView) findViewById(R.id.btn_next);
        final View buttons[] = { mBtnPrev, mBtnPlay, mBtnNext };
        for (View view : buttons) {
            view.setOnClickListener(mTransportCommandListener);
            view.setOnLongClickListener(mTransportShowSeekBarListener);
        }
        final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn();
        setEnableMarquee(screenOn);
        // Allow long-press anywhere else in this view to show the seek bar
        setOnLongClickListener(mTransportShowSeekBarListener);
    
protected voidonRestoreInstanceState(android.os.Parcelable state)

        if (!(state instanceof SavedState)) {
            super.onRestoreInstanceState(state);
            return;
        }
        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());
        mMetadata.artist = ss.artist;
        mMetadata.trackTitle = ss.trackTitle;
        mMetadata.albumTitle = ss.albumTitle;
        mMetadata.duration = ss.duration;
        mMetadata.bitmap = ss.bitmap;
        populateMetadata();
    
protected android.os.ParcelableonSaveInstanceState()

        SavedState ss = new SavedState(super.onSaveInstanceState());
        ss.artist = mMetadata.artist;
        ss.trackTitle = mMetadata.trackTitle;
        ss.albumTitle = mMetadata.albumTitle;
        ss.duration = mMetadata.duration;
        ss.bitmap = mMetadata.bitmap;
        return ss;
    
private static final booleanplaybackPositionShouldMove(int playstate)

        switch(playstate) {
            case RemoteControlClient.PLAYSTATE_STOPPED:
            case RemoteControlClient.PLAYSTATE_PAUSED:
            case RemoteControlClient.PLAYSTATE_BUFFERING:
            case RemoteControlClient.PLAYSTATE_ERROR:
            case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
            case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
                return false;
            case RemoteControlClient.PLAYSTATE_PLAYING:
            case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
            case RemoteControlClient.PLAYSTATE_REWINDING:
            default:
                return true;
        }
    
private voidpopulateMetadata()
Populates the given metadata into the view

        if (ANIMATE_TRANSITIONS && isLaidOut() && mMetadataContainer.getVisibility() == VISIBLE) {
            TransitionManager.beginDelayedTransition(mMetadataContainer, mMetadataChangeTransition);
        }

        final String remoteClientPackage = mRemoteController.getRemoteControlClientPackageName();
        Drawable badgeIcon = null;
        try {
            badgeIcon = getContext().getPackageManager().getApplicationIcon(remoteClientPackage);
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "Couldn't get remote control client package icon", e);
        }
        setBadgeIcon(badgeIcon);
        mTrackTitle.setText(!TextUtils.isEmpty(mMetadata.trackTitle)
                ? mMetadata.trackTitle : null);

        final StringBuilder sb = new StringBuilder();
        if (!TextUtils.isEmpty(mMetadata.artist)) {
            if (sb.length() != 0) {
                sb.append(" - ");
            }
            sb.append(mMetadata.artist);
        }
        if (!TextUtils.isEmpty(mMetadata.albumTitle)) {
            if (sb.length() != 0) {
                sb.append(" - ");
            }
            sb.append(mMetadata.albumTitle);
        }

        final String trackArtistAlbum = sb.toString();
        mTrackArtistAlbum.setText(!TextUtils.isEmpty(trackArtistAlbum) ?
                trackArtistAlbum : null);

        if (mMetadata.duration >= 0) {
            setSeekBarsEnabled(true);
            setSeekBarDuration(mMetadata.duration);

            final String skeleton;

            if (mMetadata.duration >= 86400000) {
                skeleton = "DDD kk mm ss";
            } else if (mMetadata.duration >= 3600000) {
                skeleton = "kk mm ss";
            } else {
                skeleton = "mm ss";
            }
            mFormat = new SimpleDateFormat(DateFormat.getBestDateTimePattern(
                    getContext().getResources().getConfiguration().locale,
                    skeleton));
            mFormat.setTimeZone(TimeZone.getTimeZone("GMT+0"));
        } else {
            setSeekBarsEnabled(false);
        }

        KeyguardUpdateMonitor.getInstance(getContext()).dispatchSetBackground(mMetadata.bitmap);
        final int flags = mTransportControlFlags;
        setVisibilityBasedOnFlag(mBtnPrev, flags, RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS);
        setVisibilityBasedOnFlag(mBtnNext, flags, RemoteControlClient.FLAG_KEY_MEDIA_NEXT);
        setVisibilityBasedOnFlag(mBtnPlay, flags,
                RemoteControlClient.FLAG_KEY_MEDIA_PLAY
                | RemoteControlClient.FLAG_KEY_MEDIA_PAUSE
                | RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE
                | RemoteControlClient.FLAG_KEY_MEDIA_STOP);

        updatePlayPauseState(mCurrentPlayState);
    
public booleanprovidesClock()

        return false;
    
voidresetToMetadata()

        if (ANIMATE_TRANSITIONS) {
            TransitionManager.beginDelayedTransition(mInfoContainer);
        }
        if (mTransientSeek.getVisibility() == VISIBLE) {
            mTransientSeek.setVisibility(INVISIBLE);
            mMetadataContainer.setVisibility(VISIBLE);
        }
        // TODO Also hide ratings, if applicable
    
voidscrubTo(int progress)

        mRemoteController.seekTo(progress);
        mTransportControlCallback.userActivity();
    
private voidsendMediaButtonClick(int keyCode)

    

        
        // 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.
        mRemoteController.sendMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
        mRemoteController.sendMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode));

        mTransportControlCallback.userActivity();
    
voidsetBadgeIcon(android.graphics.drawable.Drawable bmp)

        mBadge.setImageDrawable(bmp);

        final ColorMatrix cm = new ColorMatrix();
        cm.setSaturation(0);
        mBadge.setColorFilter(new ColorMatrixColorFilter(cm));
        mBadge.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
        mBadge.setImageAlpha(0xef);
    
private voidsetEnableMarquee(boolean enabled)

        if (DEBUG) Log.v(TAG, (enabled ? "Enable" : "Disable") + " transport text marquee");
        if (mTrackTitle != null) mTrackTitle.setSelected(enabled);
        if (mTrackArtistAlbum != null) mTrackTitle.setSelected(enabled);
    
voidsetSeekBarDuration(long duration)

        mTransientSeekBar.setMax((int) duration);
    
voidsetSeekBarsEnabled(boolean enabled)

        if (enabled == mSeekEnabled) return;

        mSeekEnabled = enabled;
        if (mTransientSeek.getVisibility() == VISIBLE && !enabled) {
            mTransientSeek.setVisibility(INVISIBLE);
            mMetadataContainer.setVisibility(VISIBLE);
            cancelResetToMetadata();
        }
    
public voidsetTransportControlCallback(KeyguardHostView.TransportControlCallback transportControlCallback)

        mTransportControlCallback = transportControlCallback;
    
private static voidsetVisibilityBasedOnFlag(android.view.View view, int flags, int flag)

        if ((flags & flag) != 0) {
            view.setVisibility(View.VISIBLE);
        } else {
            view.setVisibility(View.INVISIBLE);
        }
    
booleantryToggleSeekBar()

        if (ANIMATE_TRANSITIONS) {
            TransitionManager.beginDelayedTransition(mInfoContainer);
        }
        if (mTransientSeek.getVisibility() == VISIBLE) {
            mTransientSeek.setVisibility(INVISIBLE);
            mMetadataContainer.setVisibility(VISIBLE);
            cancelResetToMetadata();
            removeCallbacks(mUpdateSeekBars); // don't update if scrubber isn't visible
        } else {
            mTransientSeek.setVisibility(VISIBLE);
            mMetadataContainer.setVisibility(INVISIBLE);
            delayResetToMetadata();
            if (playbackPositionShouldMove(mCurrentPlayState)) {
                mUpdateSeekBars.run();
            } else {
                mUpdateSeekBars.updateOnce();
            }
        }
        mTransportControlCallback.userActivity();
        return true;
    
voidupdateMetadata(RemoteController.MetadataEditor data)

        if (isAttachedToWindow()) {
            mMetadata.artist = data.getString(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST,
                    mMetadata.artist);
            mMetadata.trackTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_TITLE,
                    mMetadata.trackTitle);
            mMetadata.albumTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_ALBUM,
                    mMetadata.albumTitle);
            mMetadata.duration = data.getLong(MediaMetadataRetriever.METADATA_KEY_DURATION, -1);
            mMetadata.bitmap = data.getBitmap(MediaMetadataEditor.BITMAP_KEY_ARTWORK,
                    mMetadata.bitmap);
            populateMetadata();
        } else {
            mPopulateMetadataWhenAttached = data;
        }
    
private voidupdatePlayPauseState(int state)

        if (DEBUG) Log.v(TAG,
                "updatePlayPauseState(), old=" + mCurrentPlayState + ", state=" + state);
        if (state == mCurrentPlayState) {
            return;
        }
        final int imageResId;
        final int imageDescId;
        switch (state) {
            case RemoteControlClient.PLAYSTATE_ERROR:
                imageResId = R.drawable.stat_sys_warning;
                // TODO use more specific image description string for warning, but here the "play"
                //      message is still valid because this button triggers a play command.
                imageDescId = R.string.keyguard_transport_play_description;
                break;

            case RemoteControlClient.PLAYSTATE_PLAYING:
                imageResId = R.drawable.ic_media_pause;
                imageDescId = R.string.keyguard_transport_pause_description;
                break;

            case RemoteControlClient.PLAYSTATE_BUFFERING:
                imageResId = R.drawable.ic_media_stop;
                imageDescId = R.string.keyguard_transport_stop_description;
                break;

            case RemoteControlClient.PLAYSTATE_PAUSED:
            default:
                imageResId = R.drawable.ic_media_play;
                imageDescId = R.string.keyguard_transport_play_description;
                break;
        }

        boolean clientSupportsSeek = mMetadata != null && mMetadata.duration > 0;
        setSeekBarsEnabled(clientSupportsSeek);

        mBtnPlay.setImageResource(imageResId);
        mBtnPlay.setContentDescription(getResources().getString(imageDescId));
        mCurrentPlayState = state;
    
booleanupdateSeekBars()

        final int position = (int) mRemoteController.getEstimatedMediaPosition();
        if (DEBUG) Log.v(TAG, "Estimated time:" + position);
        if (position >= 0) {
            mTransientSeekBar.setProgress(position);
            return true;
        }
        Log.w(TAG, "Updating seek bars; received invalid estimated media position (" +
                position + "). Disabling seek.");
        setSeekBarsEnabled(false);
        return false;
    
voidupdateSeekDisplay()

        if (mMetadata != null && mRemoteController != null && mFormat != null) {
            mTempDate.setTime(mRemoteController.getEstimatedMediaPosition());
            mTransientSeekTimeElapsed.setText(mFormat.format(mTempDate));
            mTempDate.setTime(mMetadata.duration);
            mTransientSeekTimeTotal.setText(mFormat.format(mTempDate));

            if (DEBUG) Log.d(TAG, "updateSeekDisplay timeElapsed=" + mTempDate +
                    " duration=" + mMetadata.duration);
        }
    
private voidupdateTransportControls(int transportControlFlags)

        mTransportControlFlags = transportControlFlags;
        setSeekBarsEnabled(
                (transportControlFlags & RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE) != 0);