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