diff options
| author | 2012-10-05 19:13:23 -0700 | |
|---|---|---|
| committer | 2012-10-08 21:48:30 -0700 | |
| commit | 223ce5c7326f6d13a17ccf2624605bb62864a27a (patch) | |
| tree | 60ba0b403973b3b4760e87a94759c3e3d61e3229 | |
| parent | eb7f1571ae60db17ead3a8879b4b49df306f009d (diff) | |
Implement music and user priority scheme in keyguard
This fixes an issue where the music transport hangs around indefinitely.
It used to get dismissed when music stopped playing and a notification came in.
This was due to a bug in JB.
Now that the bug is fixed, the music control hangs around indefinitely until
the application actually releases it.
Since we have widget support, we can now leave the transport control in place
for as long as it's possible to resume music (state = playing | paused).
If music is playing, we start with the trasport showing
If not, and multi-user is enabled for more than one user, we show the user switcher.
Otherwise, we show the clock.
Bug 7290707
Change-Id: I6f0553a64b7b66bac7b35eca6d8a8793c15b918c
3 files changed, 139 insertions, 48 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 6ea35138446e..840edaff5b4f 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java @@ -33,6 +33,8 @@ import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Rect; import android.os.Looper; +import android.os.Parcel; +import android.os.Parcelable; import android.os.UserManager; import android.util.AttributeSet; import android.util.Log; @@ -43,12 +45,14 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; +import android.view.View.BaseSavedState; import android.view.animation.AnimationUtils; import android.widget.RemoteViews.OnClickHandler; import android.widget.ViewFlipper; import com.android.internal.R; import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.internal.policy.impl.keyguard.KeyguardTransportControlView.SavedState; import com.android.internal.widget.LockPatternUtils; import java.io.File; @@ -64,6 +68,10 @@ public class KeyguardHostView extends KeyguardViewBase { static final int APPWIDGET_HOST_ID = 0x4B455947; private static final String KEYGUARD_WIDGET_PREFS = "keyguard_widget_prefs"; + private static final int TRANSPORT_GONE = 0; + private static final int TRANSPORT_INVISIBLE = 1; + private static final int TRANSPORT_VISIBLE = 2; + private AppWidgetHost mAppWidgetHost; private KeyguardWidgetRegion mAppWidgetRegion; private KeyguardWidgetPager mAppWidgetContainer; @@ -83,10 +91,12 @@ public class KeyguardHostView extends KeyguardViewBase { private KeyguardSecurityModel mSecurityModel; private Rect mTempRect = new Rect(); + private int mTransportState = TRANSPORT_GONE; /*package*/ interface TransportCallback { - void hide(); - void show(); + void onListenerDetached(); + void onListenerAttached(); + void onPlayStateChanged(); } /*package*/ interface UserSwitcherCallback { @@ -185,7 +195,7 @@ public class KeyguardHostView extends KeyguardViewBase { mAppWidgetHost.startListening(); maybePopulateWidgets(); disableStatusViewInteraction(); - showAppropriateWidgetPage(); + post(mSwitchPageRunnable); } private void disableStatusViewInteraction() { @@ -712,7 +722,7 @@ public class KeyguardHostView extends KeyguardViewBase { private void addDefaultWidgets() { LayoutInflater inflater = LayoutInflater.from(mContext); inflater.inflate(R.layout.keyguard_status_view, mAppWidgetContainer, true); - inflater.inflate(R.layout.keyguard_transport_control_view, mAppWidgetContainer, true); + inflater.inflate(R.layout.keyguard_transport_control_view, this, true); inflateAndAddUserSelectorWidgetIfNecessary(); initializeTransportControl(); @@ -721,16 +731,16 @@ public class KeyguardHostView extends KeyguardViewBase { private void initializeTransportControl() { mTransportControl = (KeyguardTransportControlView) findViewById(R.id.keyguard_transport_control); + mTransportControl.setVisibility(View.GONE); // 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. if (mTransportControl != null) { mTransportControl.setKeyguardCallback(new TransportCallback() { - boolean mSticky = false; @Override - public void hide() { + public void onListenerDetached() { int page = getWidgetPosition(R.id.keyguard_transport_control); - if (page != -1 && !mSticky) { + if (page != -1) { if (page == mAppWidgetContainer.getCurrentPage()) { // Switch back to clock view if music was showing. mAppWidgetContainer @@ -741,20 +751,23 @@ public class KeyguardHostView extends KeyguardViewBase { // from AudioManager KeyguardHostView.this.addView(mTransportControl); mTransportControl.setVisibility(View.GONE); + mTransportState = TRANSPORT_GONE; } } @Override - public void show() { + public void onListenerAttached() { if (getWidgetPosition(R.id.keyguard_transport_control) == -1) { KeyguardHostView.this.removeView(mTransportControl); - mAppWidgetContainer.addView(mTransportControl, - getWidgetPosition(R.id.keyguard_status_view) + 1); + mAppWidgetContainer.addView(mTransportControl, 0); mTransportControl.setVisibility(View.VISIBLE); - // Once shown, leave it showing - mSticky = true; } } + + @Override + public void onPlayStateChanged() { + mTransportControl.post(mSwitchPageRunnable); + } }); } } @@ -796,12 +809,87 @@ public class KeyguardHostView extends KeyguardViewBase { } } + Runnable mSwitchPageRunnable = new Runnable() { + @Override + public void run() { + showAppropriateWidgetPage(); + } + }; + + static class SavedState extends BaseSavedState { + int transportState; + + SavedState(Parcelable superState) { + super(superState); + } + + private SavedState(Parcel in) { + super(in); + this.transportState = in.readInt(); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + out.writeInt(this.transportState); + } + + 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() { + Parcelable superState = super.onSaveInstanceState(); + SavedState ss = new SavedState(superState); + ss.transportState = mTransportState; + return ss; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + if (!(state instanceof SavedState)) { + super.onRestoreInstanceState(state); + return; + } + SavedState ss = (SavedState) state; + super.onRestoreInstanceState(ss.getSuperState()); + mTransportState = ss.transportState; + post(mSwitchPageRunnable); + } + private void showAppropriateWidgetPage() { - int page = mAppWidgetContainer.indexOfChild(findViewById(R.id.keyguard_status_view)); - if (mAppWidgetContainer.indexOfChild(mTransportControl) != -1) { - page = mAppWidgetContainer.indexOfChild(mTransportControl); + + // The following sets the priority for showing widgets. Transport should be shown if + // music is playing, followed by the multi-user widget if enabled, followed by the + // status widget. + final int pageToShow; + if (mTransportControl.isMusicPlaying() || mTransportState == TRANSPORT_VISIBLE) { + mTransportState = TRANSPORT_VISIBLE; + pageToShow = mAppWidgetContainer.indexOfChild(mTransportControl); + } else { + UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + final View multiUserView = findViewById(R.id.keyguard_multi_user_selector); + final int multiUserPosition = mAppWidgetContainer.indexOfChild(multiUserView); + if (multiUserPosition != -1 && mUm.getUsers(true).size() > 1) { + pageToShow = multiUserPosition; + } else { + final View statusView = findViewById(R.id.keyguard_status_view); + pageToShow = mAppWidgetContainer.indexOfChild(statusView); + } + if (mTransportState == TRANSPORT_VISIBLE) { + mTransportState = TRANSPORT_INVISIBLE; + } } - mAppWidgetContainer.setCurrentPage(page); + mAppWidgetContainer.setCurrentPage(pageToShow); } private void inflateAndAddUserSelectorWidgetIfNecessary() { diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java index e2f30590150a..7e71f94eb312 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java @@ -42,7 +42,6 @@ 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; @@ -59,7 +58,7 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements 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 boolean DEBUG = true; protected static final String TAG = "TransportControlView"; private ImageView mAlbumArt; @@ -75,6 +74,7 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements private int mCurrentPlayState; private AudioManager mAudioManager; private IRemoteControlDisplayWeak mIRCD; + private boolean mMusicClientPresent = true; /** * The metadata which should be populated into the view once we've been attached @@ -112,7 +112,9 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements case MSG_SET_GENERATION_ID: if (msg.arg2 != 0) { // This means nobody is currently registered. Hide the view. - hide(); + onListenerDetached(); + } else { + onListenerAttached(); } if (DEBUG) Log.v(TAG, "New genId = " + msg.arg1 + ", clearing = " + msg.arg2); mClientGeneration = msg.arg1; @@ -193,28 +195,26 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements mIRCD = new IRemoteControlDisplayWeak(mHandler); } - protected void hide() { - if (DEBUG) Log.v(TAG, "Transport was told to hide"); + protected void onListenerDetached() { + mMusicClientPresent = false; + if (DEBUG) Log.v(TAG, "onListenerDetached()"); if (mTransportCallback != null) { - mTransportCallback.hide(); + mTransportCallback.onListenerDetached(); } else { - Log.w(TAG, "Hide music, but callback wasn't set"); + Log.w(TAG, "onListenerDetached: no callback"); } } - private void show() { - if (DEBUG) Log.v(TAG, "Transport was told to show"); + private void onListenerAttached() { + mMusicClientPresent = true; + if (DEBUG) Log.v(TAG, "onListenerAttached()"); if (mTransportCallback != null) { - mTransportCallback.show(); + mTransportCallback.onListenerAttached(); } else { - Log.w(TAG, "Show music, but callback wasn't set"); + Log.w(TAG, "onListenerAttached(): no callback"); } } - private void userActivity() { - // TODO Auto-generated method stub - } - private void updateTransportControls(int transportControlFlags) { mTransportControlFlags = transportControlFlags; } @@ -341,6 +341,11 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements updatePlayPauseState(mCurrentPlayState); } + public boolean isMusicPlaying() { + return mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING + || mCurrentPlayState == RemoteControlClient.PLAYSTATE_BUFFERING; + } + private static void setVisibilityBasedOnFlag(View view, int flags, int flag) { if ((flags & flag) != 0) { view.setVisibility(View.VISIBLE); @@ -357,7 +362,6 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements } final int imageResId; final int imageDescId; - boolean showIfHidden = false; switch (state) { case RemoteControlClient.PLAYSTATE_ERROR: imageResId = com.android.internal.R.drawable.stat_sys_warning; @@ -369,32 +373,27 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements 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; + mTransportCallback.onPlayStateChanged(); } static class SavedState extends BaseSavedState { - boolean wasShowing; + boolean clientPresent; SavedState(Parcelable superState) { super(superState); @@ -402,13 +401,13 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements private SavedState(Parcel in) { super(in); - this.wasShowing = in.readInt() != 0; + this.clientPresent = in.readInt() != 0; } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); - out.writeInt(this.wasShowing ? 1 : 0); + out.writeInt(this.clientPresent ? 1 : 0); } public static final Parcelable.Creator<SavedState> CREATOR @@ -425,24 +424,23 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements @Override public Parcelable onSaveInstanceState() { - if (DEBUG) Log.v(TAG, "onSaveInstanceState()"); Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); - ss.wasShowing = getVisibility() == View.VISIBLE; + ss.clientPresent = mMusicClientPresent; 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(); + if (ss.clientPresent) { + if (DEBUG) Log.v(TAG, "Reattaching client because it was attached"); + onListenerAttached(); } } @@ -458,7 +456,6 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements } if (keyCode != -1) { sendMediaButtonClick(keyCode); - userActivity(); } } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java index 33ff71e30785..d25444f963ef 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java @@ -48,7 +48,7 @@ import com.android.internal.widget.LockPatternUtils; * reported to this class by the current {@link KeyguardViewBase}. */ public class KeyguardViewManager { - private final static boolean DEBUG = false; + private final static boolean DEBUG = true; private static String TAG = "KeyguardViewManager"; public static boolean USE_UPPER_CASE = true; @@ -359,6 +359,12 @@ public class KeyguardViewManager { if (mKeyguardHost != null) { mKeyguardHost.setVisibility(View.GONE); + + // We really only want to preserve keyguard state for configuration changes. Hence + // we should clear state of widgets (e.g. Music) when we hide keyguard so it can + // start with a fresh state when we return. + mStateContainer.clear(); + // Don't do this right away, so we can let the view continue to animate // as it goes away. if (mKeyguardView != null) { |