summaryrefslogtreecommitdiffstats
path: root/policy
diff options
context:
space:
mode:
authorJim Miller <jaggies@google.com>2012-09-12 16:16:21 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2012-09-12 16:16:21 -0700
commit4f77268c8a75dd89303b0c6bcf6ee02a97a68d1a (patch)
tree6034dbb8847e9ecc938ee4504ac17a0c16ebc3f8 /policy
parentb11b9fc9972ba0ae7f3d13984c5c61a8d89ca142 (diff)
parentff2aa0bc8919c247bfc8e81d3f168e5064f52ffb (diff)
downloadframeworks_base-4f77268c8a75dd89303b0c6bcf6ee02a97a68d1a.zip
frameworks_base-4f77268c8a75dd89303b0c6bcf6ee02a97a68d1a.tar.gz
frameworks_base-4f77268c8a75dd89303b0c6bcf6ee02a97a68d1a.tar.bz2
Merge "Initial pass at adding Music control to new keyguard." into jb-mr1-dev
Diffstat (limited to 'policy')
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java61
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java534
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/PagedView.java4
3 files changed, 596 insertions, 3 deletions
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
index 7fa662e..fca7a35 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -41,6 +41,8 @@ import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.view.animation.AnimationUtils;
import android.widget.RemoteViews.OnClickHandler;
@@ -54,13 +56,14 @@ import java.io.File;
import java.util.List;
public class KeyguardHostView extends KeyguardViewBase {
+ private static final String TAG = "KeyguardViewHost";
+
// Use this to debug all of keyguard
public static boolean DEBUG;
static final int APPWIDGET_HOST_ID = 0x4B455947;
private static final String KEYGUARD_WIDGET_PREFS = "keyguard_widget_prefs";
- private static final String TAG = "KeyguardViewHost";
private AppWidgetHost mAppWidgetHost;
private KeyguardWidgetPager mAppWidgetContainer;
private ViewFlipper mSecurityViewContainer;
@@ -77,6 +80,12 @@ public class KeyguardHostView extends KeyguardViewBase {
private KeyguardSecurityModel mSecurityModel;
private Rect mTempRect = new Rect();
+ private KeyguardTransportControlView mTransportControl;
+
+ /*package*/ interface TransportCallback {
+ void hide();
+ void show();
+ }
public KeyguardHostView(Context context) {
this(context, null);
@@ -111,11 +120,56 @@ public class KeyguardHostView extends KeyguardViewBase {
mViewMediatorCallback.keyguardDoneDrawing();
}
+ private int getWidgetPosition(int id) {
+ final int children = mAppWidgetContainer.getChildCount();
+ for (int i = 0; i < children; i++) {
+ if (mAppWidgetContainer.getChildAt(i).getId() == id) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
@Override
protected void onFinishInflate() {
mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
mAppWidgetContainer.setVisibility(VISIBLE);
mSecurityViewContainer = (ViewFlipper) findViewById(R.id.view_flipper);
+
+ // This code manages showing/hiding the transport control. We keep it around and only
+ // add it to the hierarchy if it needs to be present.
+ mTransportControl =
+ (KeyguardTransportControlView) findViewById(R.id.keyguard_transport_control);
+ if (mTransportControl != null) {
+ mTransportControl.setKeyguardCallback(new TransportCallback() {
+ @Override
+ public void hide() {
+ int page = getWidgetPosition(R.id.keyguard_transport_control);
+ if (page != -1) {
+ if (page == mAppWidgetContainer.getCurrentPage()) {
+ // Switch back to clock view if music was showing.
+ mAppWidgetContainer
+ .setCurrentPage(getWidgetPosition(R.id.keyguard_status_view));
+ }
+ mAppWidgetContainer.removeView(mTransportControl);
+ // XXX keep view attached to hierarchy so we still get show/hide events
+ // from AudioManager
+ KeyguardHostView.this.addView(mTransportControl);
+ mTransportControl.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public void show() {
+ if (getWidgetPosition(R.id.keyguard_transport_control) == -1) {
+ KeyguardHostView.this.removeView(mTransportControl);
+ mAppWidgetContainer.addView(mTransportControl,
+ getWidgetPosition(R.id.keyguard_status_view) + 1);
+ mTransportControl.setVisibility(View.VISIBLE);
+ }
+ }
+ });
+ }
updateSecurityViews();
}
@@ -423,6 +477,7 @@ public class KeyguardHostView extends KeyguardViewBase {
@Override
public void reset() {
mIsVerifyUnlockOnly = false;
+ mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_status_view));
requestFocus();
}
@@ -639,8 +694,8 @@ public class KeyguardHostView extends KeyguardViewBase {
KeyguardWidgetFrame userswitcher = (KeyguardWidgetFrame)
LayoutInflater.from(mContext).inflate(R.layout.keyguard_multi_user_selector_widget,
mAppWidgetContainer, false);
- // add the switcher in the first position
- mAppWidgetContainer.addView(userswitcher, 0);
+ // add the switcher to the left of status view
+ mAppWidgetContainer.addView(userswitcher, getWidgetPosition(R.id.keyguard_status_view));
}
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
new file mode 100644
index 0000000..3b4ed13
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
@@ -0,0 +1,534 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import java.lang.ref.WeakReference;
+
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.media.AudioManager;
+import android.media.MediaMetadataRetriever;
+import android.media.RemoteControlClient;
+import android.media.IRemoteControlDisplay;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.text.Spannable;
+import android.text.TextUtils;
+import android.text.style.ForegroundColorSpan;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+
+import com.android.internal.R;
+import com.android.internal.policy.impl.keyguard.KeyguardHostView.TransportCallback;
+
+/**
+ * This is the widget responsible for showing music controls in keyguard.
+ */
+public class KeyguardTransportControlView extends KeyguardWidgetFrame implements OnClickListener {
+
+ private static final int MSG_UPDATE_STATE = 100;
+ private static final int MSG_SET_METADATA = 101;
+ private static final int MSG_SET_TRANSPORT_CONTROLS = 102;
+ private static final int MSG_SET_ARTWORK = 103;
+ private static final int MSG_SET_GENERATION_ID = 104;
+ private static final int MAXDIM = 512;
+ private static final int DISPLAY_TIMEOUT_MS = 5000; // 5s
+ protected static final boolean DEBUG = false;
+ protected static final String TAG = "TransportControlView";
+
+ private ImageView mAlbumArt;
+ private TextView mTrackTitle;
+ private ImageView mBtnPrev;
+ private ImageView mBtnPlay;
+ private ImageView mBtnNext;
+ private int mClientGeneration;
+ private Metadata mMetadata = new Metadata();
+ private boolean mAttached;
+ private PendingIntent mClientIntent;
+ private int mTransportControlFlags;
+ private int mCurrentPlayState;
+ private AudioManager mAudioManager;
+ private IRemoteControlDisplayWeak mIRCD;
+
+ /**
+ * The metadata which should be populated into the view once we've been attached
+ */
+ private Bundle mPopulateMetadataWhenAttached = null;
+
+ // This handler is required to ensure messages from IRCD are handled in sequence and on
+ // the UI thread.
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_STATE:
+ if (mClientGeneration == msg.arg1) updatePlayPauseState(msg.arg2);
+ break;
+
+ case MSG_SET_METADATA:
+ if (mClientGeneration == msg.arg1) updateMetadata((Bundle) msg.obj);
+ break;
+
+ case MSG_SET_TRANSPORT_CONTROLS:
+ if (mClientGeneration == msg.arg1) updateTransportControls(msg.arg2);
+ break;
+
+ case MSG_SET_ARTWORK:
+ if (mClientGeneration == msg.arg1) {
+ if (mMetadata.bitmap != null) {
+ mMetadata.bitmap.recycle();
+ }
+ mMetadata.bitmap = (Bitmap) msg.obj;
+ mAlbumArt.setImageBitmap(mMetadata.bitmap);
+ }
+ break;
+
+ case MSG_SET_GENERATION_ID:
+ if (msg.arg2 != 0) {
+ // This means nobody is currently registered. Hide the view.
+ hide();
+ }
+ if (DEBUG) Log.v(TAG, "New genId = " + msg.arg1 + ", clearing = " + msg.arg2);
+ mClientGeneration = msg.arg1;
+ mClientIntent = (PendingIntent) msg.obj;
+ break;
+
+ }
+ }
+ };
+ private TransportCallback mTransportCallback;
+
+ /**
+ * This class is required to have weak linkage to the current TransportControlView
+ * because the remote process can hold a strong reference to this binder object and
+ * we can't predict when it will be GC'd in the remote process. Without this code, it
+ * would allow a heavyweight object to be held on this side of the binder when there's
+ * no requirement to run a GC on the other side.
+ */
+ private static class IRemoteControlDisplayWeak extends IRemoteControlDisplay.Stub {
+ private WeakReference<Handler> mLocalHandler;
+
+ IRemoteControlDisplayWeak(Handler handler) {
+ mLocalHandler = new WeakReference<Handler>(handler);
+ }
+
+ public void setPlaybackState(int generationId, int state, long stateChangeTimeMs) {
+ Handler handler = mLocalHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(MSG_UPDATE_STATE, generationId, state).sendToTarget();
+ }
+ }
+
+ public void setMetadata(int generationId, Bundle metadata) {
+ Handler handler = mLocalHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(MSG_SET_METADATA, generationId, 0, metadata).sendToTarget();
+ }
+ }
+
+ public void setTransportControlFlags(int generationId, int flags) {
+ Handler handler = mLocalHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(MSG_SET_TRANSPORT_CONTROLS, generationId, flags)
+ .sendToTarget();
+ }
+ }
+
+ public void setArtwork(int generationId, Bitmap bitmap) {
+ Handler handler = mLocalHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(MSG_SET_ARTWORK, generationId, 0, bitmap).sendToTarget();
+ }
+ }
+
+ public void setAllMetadata(int generationId, Bundle metadata, Bitmap bitmap) {
+ Handler handler = mLocalHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(MSG_SET_METADATA, generationId, 0, metadata).sendToTarget();
+ handler.obtainMessage(MSG_SET_ARTWORK, generationId, 0, bitmap).sendToTarget();
+ }
+ }
+
+ public void setCurrentClientId(int clientGeneration, PendingIntent mediaIntent,
+ boolean clearing) throws RemoteException {
+ Handler handler = mLocalHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(MSG_SET_GENERATION_ID,
+ clientGeneration, (clearing ? 1 : 0), mediaIntent).sendToTarget();
+ }
+ }
+ };
+
+ public KeyguardTransportControlView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ Log.v(TAG, "Create TCV " + this);
+ mAudioManager = new AudioManager(mContext);
+ mCurrentPlayState = RemoteControlClient.PLAYSTATE_NONE; // until we get a callback
+ mIRCD = new IRemoteControlDisplayWeak(mHandler);
+ }
+
+ protected void hide() {
+ if (DEBUG) Log.v(TAG, "Transport was told to hide");
+ if (mTransportCallback != null) {
+ mTransportCallback.hide();
+ } else {
+ Log.w(TAG, "Hide music, but callback wasn't set");
+ }
+ }
+
+ private void show() {
+ if (DEBUG) Log.v(TAG, "Transport was told to show");
+ if (mTransportCallback != null) {
+ mTransportCallback.show();
+ } else {
+ Log.w(TAG, "Show music, but callback wasn't set");
+ }
+ }
+
+ private void userActivity() {
+ // TODO Auto-generated method stub
+ }
+
+ private void updateTransportControls(int transportControlFlags) {
+ mTransportControlFlags = transportControlFlags;
+ }
+
+ @Override
+ public void onFinishInflate() {
+ super.onFinishInflate();
+ mTrackTitle = (TextView) findViewById(R.id.title);
+ mTrackTitle.setSelected(true); // enable marquee
+ mAlbumArt = (ImageView) findViewById(R.id.albumart);
+ 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(this);
+ }
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (DEBUG) Log.v(TAG, "onAttachToWindow()");
+ if (mPopulateMetadataWhenAttached != null) {
+ updateMetadata(mPopulateMetadataWhenAttached);
+ mPopulateMetadataWhenAttached = null;
+ }
+ if (!mAttached) {
+ if (DEBUG) Log.v(TAG, "Registering TCV " + this);
+ mAudioManager.registerRemoteControlDisplay(mIRCD);
+ }
+ mAttached = true;
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ if (DEBUG) Log.v(TAG, "onDetachFromWindow()");
+ super.onDetachedFromWindow();
+ if (mAttached) {
+ if (DEBUG) Log.v(TAG, "Unregistering TCV " + this);
+ mAudioManager.unregisterRemoteControlDisplay(mIRCD);
+ }
+ mAttached = false;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ int dim = Math.min(MAXDIM, Math.max(getWidth(), getHeight()));
+// Log.v(TAG, "setting max bitmap size: " + dim + "x" + dim);
+// mAudioManager.remoteControlDisplayUsesBitmapSize(mIRCD, dim, dim);
+ }
+
+ class Metadata {
+ private String artist;
+ private String trackTitle;
+ private String albumTitle;
+ private Bitmap bitmap;
+
+ public String toString() {
+ return "Metadata[artist=" + artist + " trackTitle=" + trackTitle + " albumTitle=" + albumTitle + "]";
+ }
+ }
+
+ private String getMdString(Bundle data, int id) {
+ return data.getString(Integer.toString(id));
+ }
+
+ private void updateMetadata(Bundle data) {
+ if (mAttached) {
+ mMetadata.artist = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST);
+ mMetadata.trackTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_TITLE);
+ mMetadata.albumTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUM);
+ populateMetadata();
+ } else {
+ mPopulateMetadataWhenAttached = data;
+ }
+ }
+
+ /**
+ * Populates the given metadata into the view
+ */
+ private void populateMetadata() {
+ StringBuilder sb = new StringBuilder();
+ int trackTitleLength = 0;
+ if (!TextUtils.isEmpty(mMetadata.trackTitle)) {
+ sb.append(mMetadata.trackTitle);
+ trackTitleLength = mMetadata.trackTitle.length();
+ }
+ 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);
+ }
+ mTrackTitle.setText(sb.toString(), TextView.BufferType.SPANNABLE);
+ Spannable str = (Spannable) mTrackTitle.getText();
+ if (trackTitleLength != 0) {
+ str.setSpan(new ForegroundColorSpan(0xffffffff), 0, trackTitleLength,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ trackTitleLength++;
+ }
+ if (sb.length() > trackTitleLength) {
+ str.setSpan(new ForegroundColorSpan(0x7fffffff), trackTitleLength, sb.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ mAlbumArt.setImageBitmap(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);
+ }
+
+ private static void setVisibilityBasedOnFlag(View view, int flags, int flag) {
+ if ((flags & flag) != 0) {
+ view.setVisibility(View.VISIBLE);
+ } else {
+ view.setVisibility(View.GONE);
+ }
+ }
+
+ private void updatePlayPauseState(int state) {
+ if (DEBUG) Log.v(TAG,
+ "updatePlayPauseState(), old=" + mCurrentPlayState + ", state=" + state);
+ if (state == mCurrentPlayState) {
+ return;
+ }
+ final int imageResId;
+ final int imageDescId;
+ boolean showIfHidden = false;
+ switch (state) {
+ case RemoteControlClient.PLAYSTATE_ERROR:
+ imageResId = com.android.internal.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 = com.android.internal.R.string.lockscreen_transport_play_description;
+ break;
+
+ case RemoteControlClient.PLAYSTATE_PLAYING:
+ imageResId = com.android.internal.R.drawable.ic_media_pause;
+ imageDescId = com.android.internal.R.string.lockscreen_transport_pause_description;
+ showIfHidden = true;
+ break;
+
+ case RemoteControlClient.PLAYSTATE_BUFFERING:
+ imageResId = com.android.internal.R.drawable.ic_media_stop;
+ imageDescId = com.android.internal.R.string.lockscreen_transport_stop_description;
+ showIfHidden = true;
+ break;
+
+ case RemoteControlClient.PLAYSTATE_PAUSED:
+ default:
+ imageResId = com.android.internal.R.drawable.ic_media_play;
+ imageDescId = com.android.internal.R.string.lockscreen_transport_play_description;
+ showIfHidden = false;
+ break;
+ }
+ mBtnPlay.setImageResource(imageResId);
+ mBtnPlay.setContentDescription(getResources().getString(imageDescId));
+ if (showIfHidden) {
+ show();
+ }
+ mCurrentPlayState = state;
+ }
+
+ static class SavedState extends BaseSavedState {
+ boolean wasShowing;
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ private SavedState(Parcel in) {
+ super(in);
+ this.wasShowing = in.readInt() != 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeInt(this.wasShowing ? 1 : 0);
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR
+ = new Parcelable.Creator<SavedState>() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ if (DEBUG) Log.v(TAG, "onSaveInstanceState()");
+ Parcelable superState = super.onSaveInstanceState();
+ SavedState ss = new SavedState(superState);
+ ss.wasShowing = getVisibility() == View.VISIBLE;
+ return ss;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ if (DEBUG) Log.v(TAG, "onRestoreInstanceState()");
+ if (!(state instanceof SavedState)) {
+ super.onRestoreInstanceState(state);
+ return;
+ }
+ SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+ if (ss.wasShowing) {
+ show();
+ }
+ }
+
+ public void onClick(View v) {
+ int keyCode = -1;
+ if (v == mBtnPrev) {
+ keyCode = KeyEvent.KEYCODE_MEDIA_PREVIOUS;
+ } else if (v == mBtnNext) {
+ keyCode = KeyEvent.KEYCODE_MEDIA_NEXT;
+ } else if (v == mBtnPlay) {
+ keyCode = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE;
+
+ }
+ if (keyCode != -1) {
+ sendMediaButtonClick(keyCode);
+ userActivity();
+ }
+ }
+
+ private void sendMediaButtonClick(int keyCode) {
+ if (mClientIntent == null) {
+ // Shouldn't be possible because this view should be hidden in this case.
+ Log.e(TAG, "sendMediaButtonClick(): No client is currently registered");
+ return;
+ }
+ // use the registered PendingIntent that will be processed by the registered
+ // media button event receiver, which is the component of mClientIntent
+ KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
+ Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+ try {
+ mClientIntent.send(getContext(), 0, intent);
+ } catch (CanceledException e) {
+ Log.e(TAG, "Error sending intent for media button down: "+e);
+ e.printStackTrace();
+ }
+
+ keyEvent = new KeyEvent(KeyEvent.ACTION_UP, keyCode);
+ intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+ try {
+ mClientIntent.send(getContext(), 0, intent);
+ } catch (CanceledException e) {
+ Log.e(TAG, "Error sending intent for media button up: "+e);
+ e.printStackTrace();
+ }
+ }
+
+ public boolean providesClock() {
+ return false;
+ }
+
+ private boolean wasPlayingRecently(int state, long stateChangeTimeMs) {
+ switch (state) {
+ case RemoteControlClient.PLAYSTATE_PLAYING:
+ case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
+ case RemoteControlClient.PLAYSTATE_REWINDING:
+ case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
+ case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
+ case RemoteControlClient.PLAYSTATE_BUFFERING:
+ // actively playing or about to play
+ return true;
+ case RemoteControlClient.PLAYSTATE_NONE:
+ return false;
+ case RemoteControlClient.PLAYSTATE_STOPPED:
+ case RemoteControlClient.PLAYSTATE_PAUSED:
+ case RemoteControlClient.PLAYSTATE_ERROR:
+ // we have stopped playing, check how long ago
+ if (DEBUG) {
+ if ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS) {
+ Log.v(TAG, "wasPlayingRecently: time < TIMEOUT was playing recently");
+ } else {
+ Log.v(TAG, "wasPlayingRecently: time > TIMEOUT");
+ }
+ }
+ return ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS);
+ default:
+ Log.e(TAG, "Unknown playback state " + state + " in wasPlayingRecently()");
+ return false;
+ }
+ }
+
+ public void setKeyguardCallback(TransportCallback transportCallback) {
+ mTransportCallback = transportCallback;
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
index 1b46efa..fc7c90f 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
@@ -595,6 +595,10 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
@Override
public void onChildViewRemoved(View parent, View child) {
+ invalidate();
+ invalidateCachedOffsets();
+ // This prevents a crash when a child is removed that was the current page.
+ mCurrentPage = Math.min(mCurrentPage, getChildCount() - 1);
}
protected void invalidateCachedOffsets() {