summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJim Miller <jaggies@google.com>2011-08-08 22:27:58 -0700
committerJim Miller <jaggies@google.com>2011-08-29 18:29:21 -0700
commit1c18828d20807342d37000746b18a3c1696f3b2e (patch)
tree63dbcf8eba67201333fc0a4fdb025cfe385d8d50
parentf0eb7655ac897ca06034296f7674b73a65f0e8a9 (diff)
downloadframeworks_base-1c18828d20807342d37000746b18a3c1696f3b2e.zip
frameworks_base-1c18828d20807342d37000746b18a3c1696f3b2e.tar.gz
frameworks_base-1c18828d20807342d37000746b18a3c1696f3b2e.tar.bz2
Fix 5044158: Integrate music transport control into LockScreen
This integrates a new version of TransportControlView into LockScreen and adds plumbing to handle new AudioService events to show/hide the view and updates the required assets for all devices. Updated to use new AudioManager API. Since the current API only supports one RCD, the handler now lives in TransportControlView. Change-Id: I220d4dd760bef35bd84209adc3c5829bf5bc9a2c
-rw-r--r--core/java/com/android/internal/widget/TransportControlView.java367
-rw-r--r--core/res/res/drawable-hdpi/ic_lockscreen_player_background.9.pngbin0 -> 243 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_media_embed_play.pngbin1112 -> 5320 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_lockscreen_player_background.9.pngbin0 -> 230 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_embed_play.pngbin934 -> 3159 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_ff.pngbin929 -> 1410 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_fullscreen.pngbin1396 -> 2125 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_next.pngbin864 -> 1211 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_pause.pngbin561 -> 1065 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_play.pngbin918 -> 1360 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_previous.pngbin858 -> 1211 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_media_rew.pngbin997 -> 1410 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_lockscreen_player_background.9.pngbin0 -> 245 bytes
-rw-r--r--core/res/res/layout/keyguard_screen_password_landscape.xml2
-rw-r--r--core/res/res/layout/keyguard_screen_password_portrait.xml2
-rw-r--r--core/res/res/layout/keyguard_screen_tab_unlock.xml2
-rw-r--r--core/res/res/layout/keyguard_screen_tab_unlock_land.xml2
-rw-r--r--core/res/res/layout/keyguard_screen_unlock_landscape.xml2
-rw-r--r--core/res/res/layout/keyguard_screen_unlock_portrait.xml2
-rw-r--r--core/res/res/layout/keyguard_transport_control.xml131
-rw-r--r--policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java33
-rw-r--r--policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java25
-rw-r--r--policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java52
-rw-r--r--policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java3
24 files changed, 478 insertions, 145 deletions
diff --git a/core/java/com/android/internal/widget/TransportControlView.java b/core/java/com/android/internal/widget/TransportControlView.java
index 3961de3..1c47ca8 100644
--- a/core/java/com/android/internal/widget/TransportControlView.java
+++ b/core/java/com/android/internal/widget/TransportControlView.java
@@ -16,88 +16,369 @@
package com.android.internal.widget;
-import com.android.internal.R;
+import java.lang.ref.WeakReference;
+
+import com.android.internal.widget.LockScreenWidgetCallback;
+import com.android.internal.widget.LockScreenWidgetInterface;
+import android.content.ComponentName;
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.RemoteException;
+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.LinearLayout;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
-/**
- * A special widget for displaying audio playback ("transport controls") in LockScreen.
- *
- */
-public class TransportControlView extends LinearLayout implements LockScreenWidgetInterface,
- OnClickListener {
- private static final String TAG = "TransportControlView";
- static final int sViewIds[] = { R.id.control_prev, R.id.control_pauseplay, R.id.control_next };
- protected static final int AUDIO_FOCUS_CHANGED = 100;
- private LockScreenWidgetCallback mCallback;
+import com.android.internal.R;
+
+public class TransportControlView extends FrameLayout implements OnClickListener,
+ LockScreenWidgetInterface {
+
+ 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;
+ protected static final boolean DEBUG = true;
+ 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 ComponentName mClientName;
+ private int mTransportControlFlags;
+ private int mPlayState;
+ private AudioManager mAudioManager;
+ private LockScreenWidgetCallback mWidgetCallbacks;
+ 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 AUDIO_FOCUS_CHANGED:
- handleAudioFocusChange(msg.arg1);
+ 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) {
+ mMetadata.bitmap = (Bitmap) msg.obj;
+ mAlbumArt.setImageBitmap(mMetadata.bitmap);
+ }
+ break;
+
+ case MSG_SET_GENERATION_ID:
+ if (mWidgetCallbacks != null) {
+ boolean clearing = msg.arg2 != 0;
+ if (DEBUG) Log.v(TAG, "New genId = " + msg.arg1 + ", clearing = " + clearing);
+ if (!clearing) {
+ mWidgetCallbacks.requestShow(TransportControlView.this);
+ } else {
+ mWidgetCallbacks.requestHide(TransportControlView.this);
+ }
+ }
+ mClientGeneration = msg.arg1;
+ mClientName = (ComponentName) msg.obj;
+ break;
+
}
}
};
- AudioManager.OnAudioFocusChangeListener mAudioFocusChangeListener =
- new AudioManager.OnAudioFocusChangeListener() {
- public void onAudioFocusChange(final int focusChange) {
- mHandler.obtainMessage(AUDIO_FOCUS_CHANGED, focusChange, 0).sendToTarget();
+ /**
+ * 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) {
+ Handler handler = mLocalHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(MSG_UPDATE_STATE, generationId, state).sendToTarget();
}
- };
+ }
- public TransportControlView(Context context) {
- this(context, null);
- }
+ 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, ComponentName clientEventReceiver,
+ boolean clearing) throws RemoteException {
+ Handler handler = mLocalHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(MSG_SET_GENERATION_ID,
+ clientGeneration, (clearing ? 1 : 0), clientEventReceiver).sendToTarget();
+ }
+ }
+ };
public TransportControlView(Context context, AttributeSet attrs) {
super(context, attrs);
+ Log.v(TAG, "Create TCV " + this);
+ mAudioManager = new AudioManager(mContext);
+ mIRCD = new IRemoteControlDisplayWeak(mHandler);
}
- protected void handleAudioFocusChange(int focusChange) {
- // TODO
+ private void updateTransportControls(int transportControlFlags) {
+ mTransportControlFlags = transportControlFlags;
}
- public void setCallback(LockScreenWidgetCallback callback) {
- mCallback = callback;
+ @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 onFinishInflate() {
- for (int i = 0; i < sViewIds.length; i++) {
- View view = findViewById(sViewIds[i]);
- if (view != null) {
- view.setOnClickListener(this);
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ 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() {
+ 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(mBtnPrev, flags,
+ RemoteControlClient.FLAG_KEY_MEDIA_PLAY
+ | RemoteControlClient.FLAG_KEY_MEDIA_PAUSE
+ | RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE
+ | RemoteControlClient.FLAG_KEY_MEDIA_STOP);
+
+ updatePlayPauseState(mPlayState);
}
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.control_prev:
- // TODO
+ 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=" + mPlayState + ", state=" + state);
+ if (state == mPlayState) {
+ return;
+ }
+ switch (state) {
+ case RemoteControlClient.PLAYSTATE_PLAYING:
+ mBtnPlay.setImageResource(com.android.internal.R.drawable.ic_media_pause);
break;
- case R.id.control_pauseplay:
- // TODO
+ case RemoteControlClient.PLAYSTATE_BUFFERING:
+ mBtnPlay.setImageResource(com.android.internal.R.drawable.ic_media_stop);
break;
- case R.id.control_next:
- // TODO
+ case RemoteControlClient.PLAYSTATE_PAUSED:
+ default:
+ mBtnPlay.setImageResource(com.android.internal.R.drawable.ic_media_play);
break;
}
- // Have any button click extend lockscreen's timeout.
- if (mCallback != null) {
- mCallback.userActivity(this);
+ mPlayState = state;
+ }
+
+ 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);
+ if (mWidgetCallbacks != null) {
+ mWidgetCallbacks.userActivity(this);
+ }
}
}
+ private void sendMediaButtonClick(int keyCode) {
+ // TODO: target to specific player based on mClientName
+ KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
+ Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+ getContext().sendOrderedBroadcast(intent, null);
+
+ keyEvent = new KeyEvent(KeyEvent.ACTION_UP, keyCode);
+ intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+ getContext().sendOrderedBroadcast(intent, null);
+ }
+
+ public void setCallback(LockScreenWidgetCallback callback) {
+ mWidgetCallbacks = callback;
+ }
+
}
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_player_background.9.png b/core/res/res/drawable-hdpi/ic_lockscreen_player_background.9.png
new file mode 100644
index 0000000..b187358
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_lockscreen_player_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_embed_play.png b/core/res/res/drawable-hdpi/ic_media_embed_play.png
index 05778c1..23ac7e4 100644
--- a/core/res/res/drawable-hdpi/ic_media_embed_play.png
+++ b/core/res/res/drawable-hdpi/ic_media_embed_play.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_player_background.9.png b/core/res/res/drawable-mdpi/ic_lockscreen_player_background.9.png
new file mode 100644
index 0000000..8cfd1af
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_lockscreen_player_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_embed_play.png b/core/res/res/drawable-mdpi/ic_media_embed_play.png
index 3576ce5..fc5d8c6 100644
--- a/core/res/res/drawable-mdpi/ic_media_embed_play.png
+++ b/core/res/res/drawable-mdpi/ic_media_embed_play.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_ff.png b/core/res/res/drawable-mdpi/ic_media_ff.png
index 170dd2d..892772e 100644
--- a/core/res/res/drawable-mdpi/ic_media_ff.png
+++ b/core/res/res/drawable-mdpi/ic_media_ff.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_fullscreen.png b/core/res/res/drawable-mdpi/ic_media_fullscreen.png
index 960aa85..1c60e15 100644
--- a/core/res/res/drawable-mdpi/ic_media_fullscreen.png
+++ b/core/res/res/drawable-mdpi/ic_media_fullscreen.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_next.png b/core/res/res/drawable-mdpi/ic_media_next.png
index a6feed0..bbe311b 100644
--- a/core/res/res/drawable-mdpi/ic_media_next.png
+++ b/core/res/res/drawable-mdpi/ic_media_next.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_pause.png b/core/res/res/drawable-mdpi/ic_media_pause.png
index 548ba02..e4e8d86 100644
--- a/core/res/res/drawable-mdpi/ic_media_pause.png
+++ b/core/res/res/drawable-mdpi/ic_media_pause.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_play.png b/core/res/res/drawable-mdpi/ic_media_play.png
index 0fe6806..8eaf962 100644
--- a/core/res/res/drawable-mdpi/ic_media_play.png
+++ b/core/res/res/drawable-mdpi/ic_media_play.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_previous.png b/core/res/res/drawable-mdpi/ic_media_previous.png
index 0163d09..e9abc7f 100644
--- a/core/res/res/drawable-mdpi/ic_media_previous.png
+++ b/core/res/res/drawable-mdpi/ic_media_previous.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_rew.png b/core/res/res/drawable-mdpi/ic_media_rew.png
index 5489180..a5eb94a 100644
--- a/core/res/res/drawable-mdpi/ic_media_rew.png
+++ b/core/res/res/drawable-mdpi/ic_media_rew.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_player_background.9.png b/core/res/res/drawable-xhdpi/ic_lockscreen_player_background.9.png
new file mode 100644
index 0000000..7fb0cbc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_lockscreen_player_background.9.png
Binary files differ
diff --git a/core/res/res/layout/keyguard_screen_password_landscape.xml b/core/res/res/layout/keyguard_screen_password_landscape.xml
index 12df99e..694db50 100644
--- a/core/res/res/layout/keyguard_screen_password_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_password_landscape.xml
@@ -186,6 +186,8 @@
android:layout_rowSpan="6"
android:layout_columnSpan="1"
android:layout_gravity="fill"
+ android:layout_width="0dip"
+ android:layout_height="0dip"
/>
</GridLayout>
diff --git a/core/res/res/layout/keyguard_screen_password_portrait.xml b/core/res/res/layout/keyguard_screen_password_portrait.xml
index 6145e47..cf3bd42 100644
--- a/core/res/res/layout/keyguard_screen_password_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_password_portrait.xml
@@ -174,6 +174,8 @@
android:layout_rowSpan="3"
android:layout_columnSpan="1"
android:layout_gravity="fill"
+ android:layout_width="0dip"
+ android:layout_height="0dip"
/>
</GridLayout>
diff --git a/core/res/res/layout/keyguard_screen_tab_unlock.xml b/core/res/res/layout/keyguard_screen_tab_unlock.xml
index 6016d4e..a42d6cb 100644
--- a/core/res/res/layout/keyguard_screen_tab_unlock.xml
+++ b/core/res/res/layout/keyguard_screen_tab_unlock.xml
@@ -188,6 +188,8 @@
android:layout_rowSpan="4"
android:layout_columnSpan="1"
android:layout_gravity="fill"
+ android:layout_width="0dip"
+ android:layout_height="0dip"
/>
</GridLayout>
diff --git a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
index 0568dd9..b716c29 100644
--- a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
+++ b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
@@ -155,6 +155,8 @@
android:layout_rowSpan="5"
android:layout_columnSpan="1"
android:layout_gravity="fill"
+ android:layout_width="0dip"
+ android:layout_height="0dip"
/>
</GridLayout>
diff --git a/core/res/res/layout/keyguard_screen_unlock_landscape.xml b/core/res/res/layout/keyguard_screen_unlock_landscape.xml
index 9b28731..d71dbff 100644
--- a/core/res/res/layout/keyguard_screen_unlock_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_landscape.xml
@@ -156,6 +156,8 @@
android:layout_rowSpan="5"
android:layout_columnSpan="1"
android:layout_gravity="fill"
+ android:layout_width="0dip"
+ android:layout_height="0dip"
/>
</GridLayout>
diff --git a/core/res/res/layout/keyguard_screen_unlock_portrait.xml b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
index 433dda7..64c479f 100644
--- a/core/res/res/layout/keyguard_screen_unlock_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
@@ -167,6 +167,8 @@
android:layout_rowSpan="4"
android:layout_columnSpan="1"
android:layout_gravity="fill"
+ android:layout_width="0dip"
+ android:layout_height="0dip"
/>
</GridLayout>
diff --git a/core/res/res/layout/keyguard_transport_control.xml b/core/res/res/layout/keyguard_transport_control.xml
index 6308b02..2ebe5fc 100644
--- a/core/res/res/layout/keyguard_transport_control.xml
+++ b/core/res/res/layout/keyguard_transport_control.xml
@@ -1,53 +1,102 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2008, 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.
-*/
+<!-- 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.
-->
+<!-- Note: This file is meant to be included in various password unlock screens. As such,
+ LayoutParams (layout_*) for TransportControlView should *NOT* be specified here,
+ but rather as include tags for this file or the layout will break. -->
<com.android.internal.widget.TransportControlView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:gravity="bottom"
- android:orientation="horizontal"
- android:background="#a0808080"
- android:visibility="gone">
+ android:id="@+id/transport_controls"
+ android:background="@drawable/ic_lockscreen_player_background">
- <Button android:id="@+id/control_prev"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="#80ff0000"
+ <ImageView
+ android:id="@+id/albumart"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="fill"
+ android:scaleType="centerCrop"
+ android:adjustViewBounds="false"
/>
- <Space android:layout_width="0dip"
- android:layout_height="0dip"
- android:layout_weight="1"/>
-
- <Button android:id="@+id/control_pauseplay"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="#8000ff00"
- />
-
- <Space android:layout_width="0dip"
- android:layout_height="0dip"
- android:layout_weight="1"/>
-
- <Button android:id="@+id/control_next"
- android:layout_width="wrap_content"
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="#800000ff"
+ android:background="#c8000000"
+ android:layout_gravity="bottom">
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dip"
+ android:layout_marginLeft="16dip"
+ android:layout_marginRight="16dip"
+ android:gravity="center_horizontal"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textAppearance="?android:attr/textAppearanceMedium"
/>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="5dip">
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+ <ImageView
+ android:id="@+id/btn_prev"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:src="@drawable/ic_media_rew"
+ android:clickable="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:padding="10dip"/>
+ </FrameLayout>
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+ <ImageView
+ android:id="@+id/btn_play"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:clickable="true"
+ android:src="@drawable/ic_media_play"
+ android:background="?android:attr/selectableItemBackground"
+ android:padding="10dip"/>
+ </FrameLayout>
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+ <ImageView
+ android:id="@+id/btn_next"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:clickable="true"
+ android:src="@drawable/ic_media_ff"
+ android:background="?android:attr/selectableItemBackground"
+ android:padding="10dip"/>
+ </FrameLayout>
+ </LinearLayout>
+ </LinearLayout>
</com.android.internal.widget.TransportControlView>
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
index 9629702..40cc7d8 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
@@ -20,6 +20,7 @@ import com.android.internal.R;
import com.android.internal.telephony.IccCard;
import com.android.internal.telephony.IccCard.State;
import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.TransportControlView;
import com.android.internal.policy.impl.KeyguardUpdateMonitor.SimStateCallback;
import java.util.ArrayList;
@@ -52,8 +53,6 @@ class KeyguardStatusViewManager implements OnClickListener {
public static final int BATTERY_LOW_ICON = 0; //R.drawable.ic_lock_idle_low_battery;
private static final long INSTRUCTION_RESET_DELAY = 2000; // time until instruction text resets
- private static final int SHOW_WIDGET = 8;
- private static final int HIDE_WIDGET = 9;
private static final int INSTRUCTION_TEXT = 10;
private static final int CARRIER_TEXT = 11;
private static final int CARRIER_HELP_TEXT = 12;
@@ -71,7 +70,7 @@ class KeyguardStatusViewManager implements OnClickListener {
private TextView mStatus1View;
private TextView mOwnerInfoView;
private TextView mAlarmStatusView;
- private View mTransportView;
+ private TransportControlView mTransportView;
// Top-level container view for above views
private View mContainer;
@@ -162,7 +161,7 @@ class KeyguardStatusViewManager implements OnClickListener {
mStatus1View = (TextView) findViewById(R.id.status1);
mAlarmStatusView = (TextView) findViewById(R.id.alarm_status);
mOwnerInfoView = (TextView) findViewById(R.id.propertyOf);
- mTransportView = findViewById(R.id.transport);
+ mTransportView = (TransportControlView) findViewById(R.id.transport);
mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton);
if (mEmergencyCallButton != null) {
mEmergencyCallButton.setText(R.string.lockscreen_emergency_call);
@@ -192,20 +191,6 @@ class KeyguardStatusViewManager implements OnClickListener {
mUpdateMonitor.getTelephonyPlmn(), mUpdateMonitor.getTelephonySpn()));
}
- public void enterWidgetMode() {
- if (mTransportView != null) {
- mTransportView.setVisibility(View.VISIBLE);
- update(SHOW_WIDGET, null);
- }
- }
-
- public void leaveWidgetMode() {
- if (mTransportView != null) {
- mTransportView.setVisibility(View.GONE);
- update(HIDE_WIDGET, null);
- }
- }
-
private boolean inWidgetMode() {
return mTransportView != null && mTransportView.getVisibility() == View.VISIBLE;
}
@@ -248,7 +233,8 @@ class KeyguardStatusViewManager implements OnClickListener {
* @param lockIcon
*/
public void setHelpMessage(int textResId, int lockIcon) {
- mHelpMessageText = getText(textResId).toString();
+ final CharSequence tmp = getText(textResId);
+ mHelpMessageText = tmp == null ? null : tmp.toString();
update(HELP_MESSAGE_TEXT, mHelpMessageText);
}
@@ -603,15 +589,6 @@ class KeyguardStatusViewManager implements OnClickListener {
public void onPhoneStateChanged(String newState) {
updateEmergencyCallButtonState();
}
-
- public void onTransportControlStateChanged(int state) {
- // TODO: define what state means
- if (state == 0) {
- leaveWidgetMode();
- } else {
- enterWidgetMode();
- }
- }
};
private SimStateCallback mSimStateCallback = new SimStateCallback() {
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
index 77f1932..2955de3 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
@@ -25,6 +25,7 @@ import static android.os.BatteryManager.BATTERY_STATUS_CHARGING;
import static android.os.BatteryManager.BATTERY_STATUS_FULL;
import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
import android.media.AudioManager;
+import android.media.IRemoteControlClient;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.Message;
@@ -93,8 +94,6 @@ public class KeyguardUpdateMonitor {
private static final int MSG_SIM_STATE_CHANGE = 304;
private static final int MSG_RINGER_MODE_CHANGED = 305;
private static final int MSG_PHONE_STATE_CHANGED = 306;
- private static final int MSG_TRANSPORT_CONTROL_STATE_CHANGED = 307;
-
/**
* When we receive a
@@ -171,9 +170,6 @@ public class KeyguardUpdateMonitor {
case MSG_PHONE_STATE_CHANGED:
handlePhoneStateChanged((String)msg.obj);
break;
- case MSG_TRANSPORT_CONTROL_STATE_CHANGED:
- handleTransportControlStateChanged(msg.arg1);
- break;
}
}
};
@@ -263,23 +259,10 @@ public class KeyguardUpdateMonitor {
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
mHandler.sendMessage(mHandler.obtainMessage(MSG_PHONE_STATE_CHANGED, state));
}
- // TODO
- else if ("android.media.TRANSPORT_CONTROL_CHANGED".equals(action)) {
- int state = intent.getIntExtra("state", 0);
- mHandler.sendMessage(mHandler.obtainMessage(MSG_TRANSPORT_CONTROL_STATE_CHANGED,
- state));
- }
}
}, filter);
}
- protected void handleTransportControlStateChanged(int state) {
- if (DEBUG) Log.d(TAG, "handleTransportControlStateChanged()");
- for (int i = 0; i < mInfoCallbacks.size(); i++) {
- mInfoCallbacks.get(i).onTransportControlStateChanged(state);
- }
- }
-
protected void handlePhoneStateChanged(String newState) {
if (DEBUG) Log.d(TAG, "handlePhoneStateChanged(" + newState + ")");
for (int i = 0; i < mInfoCallbacks.size(); i++) {
@@ -465,11 +448,6 @@ public class KeyguardUpdateMonitor {
*/
void onPhoneStateChanged(String newState);
- /**
- * Called when AudioService informs us of a change to the transport control client.
- *
- */
- void onTransportControlStateChanged(int state);
}
/**
@@ -567,4 +545,5 @@ public class KeyguardUpdateMonitor {
public void reportFailedAttempt() {
mFailedAttempts++;
}
+
}
diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
index 5c85903..9c14734 100644
--- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
+++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
@@ -20,6 +20,8 @@ import com.android.internal.R;
import com.android.internal.policy.impl.LockPatternKeyguardView.UnlockMode;
import com.android.internal.telephony.IccCard;
import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockScreenWidgetCallback;
+import com.android.internal.widget.TransportControlView;
import android.accounts.Account;
import android.accounts.AccountManager;
@@ -66,6 +68,8 @@ import java.io.IOException;
*/
public class LockPatternKeyguardView extends KeyguardViewBase {
+ private static final int TRANSPORT_USERACTIVITY_TIMEOUT = 10000;
+
static final boolean DEBUG_CONFIGURATION = false;
// time after launching EmergencyDialer before the screen goes blank.
@@ -179,6 +183,22 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
}
};
+ private LockScreenWidgetCallback mWidgetCallback = new LockScreenWidgetCallback() {
+ public void userActivity(View self) {
+ mKeyguardScreenCallback.pokeWakelock(TRANSPORT_USERACTIVITY_TIMEOUT);
+ }
+
+ public void requestShow(View view) {
+ if (DEBUG) Log.v(TAG, "View " + view + " requested show transports");
+ view.setVisibility(View.VISIBLE);
+ }
+
+ public void requestHide(View view) {
+ if (DEBUG) Log.v(TAG, "View " + view + " requested hide transports");
+ view.setVisibility(View.GONE);
+ }
+ };
+
/**
* @return Whether we are stuck on the lock screen because the sim is
* missing.
@@ -490,6 +510,14 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
super.onDetachedFromWindow();
}
+ protected void onConfigurationChanged(Configuration newConfig) {
+ Resources resources = getResources();
+ mShowLockBeforeUnlock = resources.getBoolean(R.bool.config_enableLockBeforeUnlockScreen);
+ mConfiguration = newConfig;
+ if (DEBUG_CONFIGURATION) Log.v(TAG, "**** re-creating lock screen since config changed");
+ updateScreen(mMode, true /* force */);
+ }
+
@Override
protected boolean dispatchHoverEvent(MotionEvent event) {
// Do not let the screen to get locked while the user is disabled and touch
@@ -502,14 +530,6 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
return super.dispatchHoverEvent(event);
}
- protected void onConfigurationChanged(Configuration newConfig) {
- Resources resources = getResources();
- mShowLockBeforeUnlock = resources.getBoolean(R.bool.config_enableLockBeforeUnlockScreen);
- mConfiguration = newConfig;
- if (DEBUG_CONFIGURATION) Log.v(TAG, "**** re-creating lock screen since config changed");
- updateScreen(mMode, true /* force */);
- }
-
@Override
public void wakeWhenReadyTq(int keyCode) {
if (DEBUG) Log.d(TAG, "onWakeKey");
@@ -639,12 +659,14 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
}
View createLockScreen() {
- return new LockScreen(
+ View lockView = new LockScreen(
mContext,
mConfiguration,
mLockPatternUtils,
mUpdateMonitor,
mKeyguardScreenCallback);
+ initializeTransportControlView(lockView);
+ return lockView;
}
View createUnlockScreenFor(UnlockMode unlockMode) {
@@ -710,10 +732,22 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
} else {
throw new IllegalArgumentException("unknown unlock mode " + unlockMode);
}
+ initializeTransportControlView(unlockView);
mUnlockScreenMode = unlockMode;
return unlockView;
}
+ private void initializeTransportControlView(View view) {
+ com.android.internal.widget.TransportControlView tcv =
+ (TransportControlView) view.findViewById(R.id.transport);
+ if (tcv == null) {
+ if (DEBUG) Log.w(TAG, "Couldn't find transport control widget");
+ } else {
+ tcv.setVisibility(View.GONE); // hide tcv until we get the callback below to show it.
+ tcv.setCallback(mWidgetCallback);
+ }
+ }
+
/**
* Given the current state of things, what should be the initial mode of
* the lock screen (lock or unlock).
diff --git a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
index ce3bc74..2f2d3b7 100644
--- a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
@@ -347,7 +347,8 @@ public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
// Check if this was the result of hitting the enter key
- if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE) {
+ if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE
+ || actionId == EditorInfo.IME_ACTION_NEXT) {
verifyPasswordAndUnlock();
return true;
}