1ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller/*
2ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller * Copyright (C) 2011 The Android Open Source Project
3ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller *
4ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller * Licensed under the Apache License, Version 2.0 (the "License");
5ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller * you may not use this file except in compliance with the License.
6ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller * You may obtain a copy of the License at
7ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller *
8ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller *      http://www.apache.org/licenses/LICENSE-2.0
9ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller *
10ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller * Unless required by applicable law or agreed to in writing, software
11ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller * distributed under the License is distributed on an "AS IS" BASIS,
12ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller * See the License for the specific language governing permissions and
14ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller * limitations under the License.
15ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller */
16ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
17ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerpackage com.android.internal.policy.impl.keyguard;
18ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
19ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.app.PendingIntent;
20ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.app.PendingIntent.CanceledException;
21ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.content.Context;
22ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.content.Intent;
23ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.graphics.Bitmap;
24ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.media.AudioManager;
251254f2f42f7173ef51d0034975ab5cb7d44f8209Michael Jurkaimport android.media.IRemoteControlDisplay;
26ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.media.MediaMetadataRetriever;
27ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.media.RemoteControlClient;
28ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.os.Bundle;
29ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.os.Handler;
30ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.os.Message;
31ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.os.Parcel;
32ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.os.Parcelable;
33ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.os.RemoteException;
34ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.os.SystemClock;
35ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.text.Spannable;
36ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.text.TextUtils;
37ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.text.style.ForegroundColorSpan;
38ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.util.AttributeSet;
39ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.util.Log;
40ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.view.KeyEvent;
41ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.view.View;
42ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.view.View.OnClickListener;
43bdca3c0a21ba15e8bb6d7ef0ca1b7e8024f98f19Jim Millerimport android.widget.FrameLayout;
44ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.widget.ImageView;
45ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport android.widget.TextView;
46ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
47ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Millerimport com.android.internal.R;
481254f2f42f7173ef51d0034975ab5cb7d44f8209Michael Jurka
491254f2f42f7173ef51d0034975ab5cb7d44f8209Michael Jurkaimport java.lang.ref.WeakReference;
50ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller/**
51ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller * This is the widget responsible for showing music controls in keyguard.
52ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller */
53bdca3c0a21ba15e8bb6d7ef0ca1b7e8024f98f19Jim Millerpublic class KeyguardTransportControlView extends FrameLayout implements OnClickListener {
54ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
55ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private static final int MSG_UPDATE_STATE = 100;
56ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private static final int MSG_SET_METADATA = 101;
57ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private static final int MSG_SET_TRANSPORT_CONTROLS = 102;
58ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private static final int MSG_SET_ARTWORK = 103;
59ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private static final int MSG_SET_GENERATION_ID = 104;
60ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private static final int DISPLAY_TIMEOUT_MS = 5000; // 5s
6171b3cd56a618e66a407081ee46bf3f64bd0e9582Jim Miller    protected static final boolean DEBUG = false;
62ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    protected static final String TAG = "TransportControlView";
63ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
64ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private ImageView mAlbumArt;
65ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private TextView mTrackTitle;
66ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private ImageView mBtnPrev;
67ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private ImageView mBtnPlay;
68ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private ImageView mBtnNext;
69ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private int mClientGeneration;
70ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private Metadata mMetadata = new Metadata();
71ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private boolean mAttached;
72ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private PendingIntent mClientIntent;
73ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private int mTransportControlFlags;
74ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private int mCurrentPlayState;
75ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private AudioManager mAudioManager;
76ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private IRemoteControlDisplayWeak mIRCD;
77223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller    private boolean mMusicClientPresent = true;
78ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
79ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    /**
80ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller     * The metadata which should be populated into the view once we've been attached
81ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller     */
82ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private Bundle mPopulateMetadataWhenAttached = null;
83ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
84ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    // This handler is required to ensure messages from IRCD are handled in sequence and on
85ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    // the UI thread.
86ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private Handler mHandler = new Handler() {
87ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        @Override
88ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        public void handleMessage(Message msg) {
89ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            switch (msg.what) {
90ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            case MSG_UPDATE_STATE:
91ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                if (mClientGeneration == msg.arg1) updatePlayPauseState(msg.arg2);
92ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                break;
93ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
94ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            case MSG_SET_METADATA:
95ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                if (mClientGeneration == msg.arg1) updateMetadata((Bundle) msg.obj);
96ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                break;
97ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
98ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            case MSG_SET_TRANSPORT_CONTROLS:
99ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                if (mClientGeneration == msg.arg1) updateTransportControls(msg.arg2);
100ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                break;
101ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
102ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            case MSG_SET_ARTWORK:
103ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                if (mClientGeneration == msg.arg1) {
104ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                    if (mMetadata.bitmap != null) {
105ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                        mMetadata.bitmap.recycle();
106ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                    }
107ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                    mMetadata.bitmap = (Bitmap) msg.obj;
108ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                    mAlbumArt.setImageBitmap(mMetadata.bitmap);
109ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                }
110ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                break;
111ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
112ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            case MSG_SET_GENERATION_ID:
113ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                if (msg.arg2 != 0) {
114ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                    // This means nobody is currently registered. Hide the view.
115223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller                    onListenerDetached();
116223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller                } else {
117223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller                    onListenerAttached();
118ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                }
119ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                if (DEBUG) Log.v(TAG, "New genId = " + msg.arg1 + ", clearing = " + msg.arg2);
120ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                mClientGeneration = msg.arg1;
121ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                mClientIntent = (PendingIntent) msg.obj;
122ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                break;
123ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
124ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            }
125ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
126ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    };
127109f1fd80c90409c0d7f21d49989641dfdf2ad1bJim Miller    private KeyguardHostView.TransportCallback mTransportCallback;
128ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
129ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    /**
130ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller     * This class is required to have weak linkage to the current TransportControlView
131ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller     * because the remote process can hold a strong reference to this binder object and
132ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller     * we can't predict when it will be GC'd in the remote process. Without this code, it
133ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller     * would allow a heavyweight object to be held on this side of the binder when there's
134ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller     * no requirement to run a GC on the other side.
135ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller     */
136ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private static class IRemoteControlDisplayWeak extends IRemoteControlDisplay.Stub {
137ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        private WeakReference<Handler> mLocalHandler;
138ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
139ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        IRemoteControlDisplayWeak(Handler handler) {
140ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            mLocalHandler = new WeakReference<Handler>(handler);
141ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
142ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
143ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        public void setPlaybackState(int generationId, int state, long stateChangeTimeMs) {
144ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            Handler handler = mLocalHandler.get();
145ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            if (handler != null) {
146ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                handler.obtainMessage(MSG_UPDATE_STATE, generationId, state).sendToTarget();
147ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            }
148ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
149ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
150ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        public void setMetadata(int generationId, Bundle metadata) {
151ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            Handler handler = mLocalHandler.get();
152ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            if (handler != null) {
153ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                handler.obtainMessage(MSG_SET_METADATA, generationId, 0, metadata).sendToTarget();
154ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            }
155ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
156ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
157ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        public void setTransportControlFlags(int generationId, int flags) {
158ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            Handler handler = mLocalHandler.get();
159ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            if (handler != null) {
160ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                handler.obtainMessage(MSG_SET_TRANSPORT_CONTROLS, generationId, flags)
161ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                        .sendToTarget();
162ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            }
163ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
164ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
165ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        public void setArtwork(int generationId, Bitmap bitmap) {
166ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            Handler handler = mLocalHandler.get();
167ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            if (handler != null) {
168ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                handler.obtainMessage(MSG_SET_ARTWORK, generationId, 0, bitmap).sendToTarget();
169ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            }
170ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
171ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
172ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        public void setAllMetadata(int generationId, Bundle metadata, Bitmap bitmap) {
173ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            Handler handler = mLocalHandler.get();
174ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            if (handler != null) {
175ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                handler.obtainMessage(MSG_SET_METADATA, generationId, 0, metadata).sendToTarget();
176ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                handler.obtainMessage(MSG_SET_ARTWORK, generationId, 0, bitmap).sendToTarget();
177ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            }
178ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
179ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
180ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        public void setCurrentClientId(int clientGeneration, PendingIntent mediaIntent,
181ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                boolean clearing) throws RemoteException {
182ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            Handler handler = mLocalHandler.get();
183ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            if (handler != null) {
184ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                handler.obtainMessage(MSG_SET_GENERATION_ID,
185ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                    clientGeneration, (clearing ? 1 : 0), mediaIntent).sendToTarget();
186ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            }
187ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
188ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    };
189ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
190ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    public KeyguardTransportControlView(Context context, AttributeSet attrs) {
191ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        super(context, attrs);
192ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        Log.v(TAG, "Create TCV " + this);
193ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        mAudioManager = new AudioManager(mContext);
194ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        mCurrentPlayState = RemoteControlClient.PLAYSTATE_NONE; // until we get a callback
195ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        mIRCD = new IRemoteControlDisplayWeak(mHandler);
196ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    }
197ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
198223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller    protected void onListenerDetached() {
199223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller        mMusicClientPresent = false;
200223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller        if (DEBUG) Log.v(TAG, "onListenerDetached()");
201ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        if (mTransportCallback != null) {
202223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller            mTransportCallback.onListenerDetached();
203ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        } else {
204223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller            Log.w(TAG, "onListenerDetached: no callback");
205ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
206ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    }
207ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
208223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller    private void onListenerAttached() {
209223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller        mMusicClientPresent = true;
210223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller        if (DEBUG) Log.v(TAG, "onListenerAttached()");
211ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        if (mTransportCallback != null) {
212223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller            mTransportCallback.onListenerAttached();
213ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        } else {
214223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller            Log.w(TAG, "onListenerAttached(): no callback");
215ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
216ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    }
217ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
218ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private void updateTransportControls(int transportControlFlags) {
219ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        mTransportControlFlags = transportControlFlags;
220ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    }
221ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
222ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    @Override
223ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    public void onFinishInflate() {
224ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        super.onFinishInflate();
225ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        mTrackTitle = (TextView) findViewById(R.id.title);
226ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        mTrackTitle.setSelected(true); // enable marquee
227ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        mAlbumArt = (ImageView) findViewById(R.id.albumart);
228ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        mBtnPrev = (ImageView) findViewById(R.id.btn_prev);
229ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        mBtnPlay = (ImageView) findViewById(R.id.btn_play);
230ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        mBtnNext = (ImageView) findViewById(R.id.btn_next);
231ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        final View buttons[] = { mBtnPrev, mBtnPlay, mBtnNext };
232ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        for (View view : buttons) {
233ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            view.setOnClickListener(this);
234ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
235ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    }
236ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
237ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    @Override
238ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    public void onAttachedToWindow() {
239ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        super.onAttachedToWindow();
240ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        if (DEBUG) Log.v(TAG, "onAttachToWindow()");
241ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        if (mPopulateMetadataWhenAttached != null) {
242ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            updateMetadata(mPopulateMetadataWhenAttached);
243ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            mPopulateMetadataWhenAttached = null;
244ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
245ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        if (!mAttached) {
246ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            if (DEBUG) Log.v(TAG, "Registering TCV " + this);
247ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            mAudioManager.registerRemoteControlDisplay(mIRCD);
248ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
249ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        mAttached = true;
250ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    }
251ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
252ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    @Override
253ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    public void onDetachedFromWindow() {
254ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        if (DEBUG) Log.v(TAG, "onDetachFromWindow()");
255ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        super.onDetachedFromWindow();
256ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        if (mAttached) {
257ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            if (DEBUG) Log.v(TAG, "Unregistering TCV " + this);
258ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            mAudioManager.unregisterRemoteControlDisplay(mIRCD);
259ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
260ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        mAttached = false;
261ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    }
262ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
263ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    class Metadata {
264ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        private String artist;
265ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        private String trackTitle;
266ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        private String albumTitle;
267ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        private Bitmap bitmap;
268ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
269ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        public String toString() {
270ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            return "Metadata[artist=" + artist + " trackTitle=" + trackTitle + " albumTitle=" + albumTitle + "]";
271ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
272ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    }
273ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
274ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private String getMdString(Bundle data, int id) {
275ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        return data.getString(Integer.toString(id));
276ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    }
277ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
278ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private void updateMetadata(Bundle data) {
279ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        if (mAttached) {
280ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            mMetadata.artist = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST);
281ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            mMetadata.trackTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_TITLE);
282ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            mMetadata.albumTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUM);
283ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            populateMetadata();
284ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        } else {
285ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            mPopulateMetadataWhenAttached = data;
286ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
287ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    }
288ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
289ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    /**
290ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller     * Populates the given metadata into the view
291ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller     */
292ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private void populateMetadata() {
293ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        StringBuilder sb = new StringBuilder();
294ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        int trackTitleLength = 0;
295ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        if (!TextUtils.isEmpty(mMetadata.trackTitle)) {
296ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            sb.append(mMetadata.trackTitle);
297ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            trackTitleLength = mMetadata.trackTitle.length();
298ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
299ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        if (!TextUtils.isEmpty(mMetadata.artist)) {
300ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            if (sb.length() != 0) {
301ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                sb.append(" - ");
302ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            }
303ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            sb.append(mMetadata.artist);
304ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
305ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        if (!TextUtils.isEmpty(mMetadata.albumTitle)) {
306ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            if (sb.length() != 0) {
307ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                sb.append(" - ");
308ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            }
309ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            sb.append(mMetadata.albumTitle);
310ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
311ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        mTrackTitle.setText(sb.toString(), TextView.BufferType.SPANNABLE);
312ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        Spannable str = (Spannable) mTrackTitle.getText();
313ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        if (trackTitleLength != 0) {
314ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            str.setSpan(new ForegroundColorSpan(0xffffffff), 0, trackTitleLength,
315ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
316ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            trackTitleLength++;
317ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
318ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        if (sb.length() > trackTitleLength) {
319ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            str.setSpan(new ForegroundColorSpan(0x7fffffff), trackTitleLength, sb.length(),
320ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
321ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
322ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
323ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        mAlbumArt.setImageBitmap(mMetadata.bitmap);
324ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        final int flags = mTransportControlFlags;
325ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        setVisibilityBasedOnFlag(mBtnPrev, flags, RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS);
326ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        setVisibilityBasedOnFlag(mBtnNext, flags, RemoteControlClient.FLAG_KEY_MEDIA_NEXT);
327ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        setVisibilityBasedOnFlag(mBtnPlay, flags,
328ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                RemoteControlClient.FLAG_KEY_MEDIA_PLAY
329ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                | RemoteControlClient.FLAG_KEY_MEDIA_PAUSE
330ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                | RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE
331ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                | RemoteControlClient.FLAG_KEY_MEDIA_STOP);
332ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
333ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        updatePlayPauseState(mCurrentPlayState);
334ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    }
335ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
336223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller    public boolean isMusicPlaying() {
337223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller       return mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING
338223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller               || mCurrentPlayState == RemoteControlClient.PLAYSTATE_BUFFERING;
339223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller    }
340223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller
341ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private static void setVisibilityBasedOnFlag(View view, int flags, int flag) {
342ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        if ((flags & flag) != 0) {
343ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            view.setVisibility(View.VISIBLE);
344ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        } else {
345ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            view.setVisibility(View.GONE);
346ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
347ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    }
348ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
349ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private void updatePlayPauseState(int state) {
350ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        if (DEBUG) Log.v(TAG,
351ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                "updatePlayPauseState(), old=" + mCurrentPlayState + ", state=" + state);
352ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        if (state == mCurrentPlayState) {
353ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            return;
354ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
355ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        final int imageResId;
356ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        final int imageDescId;
357ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        switch (state) {
358ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            case RemoteControlClient.PLAYSTATE_ERROR:
359ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                imageResId = com.android.internal.R.drawable.stat_sys_warning;
360ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                // TODO use more specific image description string for warning, but here the "play"
361ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                //      message is still valid because this button triggers a play command.
362ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                imageDescId = com.android.internal.R.string.lockscreen_transport_play_description;
363ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                break;
364ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
365ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            case RemoteControlClient.PLAYSTATE_PLAYING:
366ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                imageResId = com.android.internal.R.drawable.ic_media_pause;
367ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                imageDescId = com.android.internal.R.string.lockscreen_transport_pause_description;
368ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                break;
369ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
370ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            case RemoteControlClient.PLAYSTATE_BUFFERING:
371ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                imageResId = com.android.internal.R.drawable.ic_media_stop;
372ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                imageDescId = com.android.internal.R.string.lockscreen_transport_stop_description;
373ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                break;
374ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
375ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            case RemoteControlClient.PLAYSTATE_PAUSED:
376ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            default:
377ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                imageResId = com.android.internal.R.drawable.ic_media_play;
378ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                imageDescId = com.android.internal.R.string.lockscreen_transport_play_description;
379ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                break;
380ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
381ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        mBtnPlay.setImageResource(imageResId);
382ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        mBtnPlay.setContentDescription(getResources().getString(imageDescId));
383ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        mCurrentPlayState = state;
384223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller        mTransportCallback.onPlayStateChanged();
385ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    }
386ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
387ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    static class SavedState extends BaseSavedState {
388223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller        boolean clientPresent;
389ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
390ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        SavedState(Parcelable superState) {
391ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            super(superState);
392ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
393ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
394ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        private SavedState(Parcel in) {
395ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            super(in);
396223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller            this.clientPresent = in.readInt() != 0;
397ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
398ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
399ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        @Override
400ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        public void writeToParcel(Parcel out, int flags) {
401ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            super.writeToParcel(out, flags);
402223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller            out.writeInt(this.clientPresent ? 1 : 0);
403ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
404ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
405ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        public static final Parcelable.Creator<SavedState> CREATOR
406ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                = new Parcelable.Creator<SavedState>() {
407ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            public SavedState createFromParcel(Parcel in) {
408ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                return new SavedState(in);
409ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            }
410ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
411ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            public SavedState[] newArray(int size) {
412ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                return new SavedState[size];
413ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            }
414ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        };
415ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    }
416ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
417ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    @Override
418ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    public Parcelable onSaveInstanceState() {
419ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        Parcelable superState = super.onSaveInstanceState();
420ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        SavedState ss = new SavedState(superState);
421223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller        ss.clientPresent = mMusicClientPresent;
422ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        return ss;
423ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    }
424ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
425ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    @Override
426ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    public void onRestoreInstanceState(Parcelable state) {
427ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        if (!(state instanceof SavedState)) {
428ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            super.onRestoreInstanceState(state);
429ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            return;
430ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
431ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        SavedState ss = (SavedState) state;
432ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        super.onRestoreInstanceState(ss.getSuperState());
433223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller        if (ss.clientPresent) {
434223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller            if (DEBUG) Log.v(TAG, "Reattaching client because it was attached");
435223ce5c7326f6d13a17ccf2624605bb62864a27aJim Miller            onListenerAttached();
436ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
437ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    }
438ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
439ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    public void onClick(View v) {
440ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        int keyCode = -1;
441ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        if (v == mBtnPrev) {
442ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            keyCode = KeyEvent.KEYCODE_MEDIA_PREVIOUS;
443ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        } else if (v == mBtnNext) {
444ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            keyCode = KeyEvent.KEYCODE_MEDIA_NEXT;
445ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        } else if (v == mBtnPlay) {
446ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            keyCode = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE;
447ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
448ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
449ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        if (keyCode != -1) {
450ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            sendMediaButtonClick(keyCode);
451ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
452ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    }
453ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
454ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private void sendMediaButtonClick(int keyCode) {
455ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        if (mClientIntent == null) {
456ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            // Shouldn't be possible because this view should be hidden in this case.
457ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            Log.e(TAG, "sendMediaButtonClick(): No client is currently registered");
458ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            return;
459ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
460ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        // use the registered PendingIntent that will be processed by the registered
461ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        //    media button event receiver, which is the component of mClientIntent
462ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
463ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
464ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
465ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        try {
466ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            mClientIntent.send(getContext(), 0, intent);
467ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        } catch (CanceledException e) {
468ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            Log.e(TAG, "Error sending intent for media button down: "+e);
469ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            e.printStackTrace();
470ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
471ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
472ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        keyEvent = new KeyEvent(KeyEvent.ACTION_UP, keyCode);
473ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
474ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
475ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        try {
476ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            mClientIntent.send(getContext(), 0, intent);
477ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        } catch (CanceledException e) {
478ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            Log.e(TAG, "Error sending intent for media button up: "+e);
479ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            e.printStackTrace();
480ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
481ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    }
482ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
483ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    public boolean providesClock() {
484ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        return false;
485ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    }
486ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
487ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    private boolean wasPlayingRecently(int state, long stateChangeTimeMs) {
488ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        switch (state) {
489ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            case RemoteControlClient.PLAYSTATE_PLAYING:
490ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
491ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            case RemoteControlClient.PLAYSTATE_REWINDING:
492ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
493ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
494ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            case RemoteControlClient.PLAYSTATE_BUFFERING:
495ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                // actively playing or about to play
496ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                return true;
497ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            case RemoteControlClient.PLAYSTATE_NONE:
498ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                return false;
499ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            case RemoteControlClient.PLAYSTATE_STOPPED:
500ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            case RemoteControlClient.PLAYSTATE_PAUSED:
501ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            case RemoteControlClient.PLAYSTATE_ERROR:
502ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                // we have stopped playing, check how long ago
503ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                if (DEBUG) {
504ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                    if ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS) {
505ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                        Log.v(TAG, "wasPlayingRecently: time < TIMEOUT was playing recently");
506ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                    } else {
507ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                        Log.v(TAG, "wasPlayingRecently: time > TIMEOUT");
508ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                    }
509ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                }
510ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                return ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS);
511ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller            default:
512ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                Log.e(TAG, "Unknown playback state " + state + " in wasPlayingRecently()");
513ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller                return false;
514ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        }
515ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    }
516ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller
517109f1fd80c90409c0d7f21d49989641dfdf2ad1bJim Miller    public void setKeyguardCallback(KeyguardHostView.TransportCallback transportCallback) {
518ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller        mTransportCallback = transportCallback;
519ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller    }
520ff2aa0bc8919c247bfc8e81d3f168e5064f52ffbJim Miller}
521