diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 6644c9c5..24f3d984 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -8,12 +8,18 @@
android:name="Tumult">
+ android:theme="@style/NoBackground"
+ android:launchMode="singleTop" >
+
diff --git a/res/layout/nowplaying.xml b/res/layout/nowplaying.xml
index 5d0dbcc4..8d05bcb7 100644
--- a/res/layout/nowplaying.xml
+++ b/res/layout/nowplaying.xml
@@ -36,27 +36,6 @@
android:layout_gravity="bottom|center_horizontal"
android:background="#a000"
android:orientation="horizontal">
-
-
-
+
\ No newline at end of file
diff --git a/res/layout/playback_buttons.xml b/res/layout/playback_buttons.xml
new file mode 100644
index 00000000..f8887d01
--- /dev/null
+++ b/res/layout/playback_buttons.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/remote_dialog.xml b/res/layout/remote_dialog.xml
new file mode 100644
index 00000000..49528798
--- /dev/null
+++ b/res/layout/remote_dialog.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 97c9bf23..da1ff65e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1,7 +1,12 @@
Tumult
- Audio only plays when a headset is plugged in
+
Headset only
+ Audio only plays when a headset is plugged in
Audio may play without a headset plugged in
+
+ Use Remote Player
+ Clicking the notification will open a mini-player dialog
+ Clicking the notification will open a the full player activity
\ No newline at end of file
diff --git a/res/values/theme.xml b/res/values/theme.xml
index c952f7a4..356b7d2d 100644
--- a/res/values/theme.xml
+++ b/res/values/theme.xml
@@ -1,5 +1,6 @@
-
\ No newline at end of file
diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml
index 0e168212..2107f733 100644
--- a/res/xml/preferences.xml
+++ b/res/xml/preferences.xml
@@ -8,4 +8,10 @@
android:defaultValue="false"
android:summaryOn="@string/headset_only_summary_on"
android:summaryOff="@string/headset_only_summary_off" />
+
\ No newline at end of file
diff --git a/src/org/kreed/tumult/CoverView.java b/src/org/kreed/tumult/CoverView.java
index fede80b1..0068f68b 100755
--- a/src/org/kreed/tumult/CoverView.java
+++ b/src/org/kreed/tumult/CoverView.java
@@ -9,7 +9,11 @@ import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
@@ -17,26 +21,20 @@ import android.widget.Scroller;
public class CoverView extends View {
private static final int SNAP_VELOCITY = 1000;
+ private static final int STORE_SIZE = 3;
private Scroller mScroller;
private VelocityTracker mVelocityTracker;
private float mLastMotionX;
private float mStartX;
private float mStartY;
-
- private CoverViewWatcher mListener;
+ private IPlaybackService mService;
Song[] mSongs = new Song[3];
private Bitmap[] mBitmaps = new Bitmap[3];
private int mTentativeCover = -1;
- public interface CoverViewWatcher {
- public void next();
- public void previous();
- public void clicked();
- }
-
public CoverView(Context context, AttributeSet attributes)
{
super(context, attributes);
@@ -44,14 +42,14 @@ public class CoverView extends View {
mScroller = new Scroller(context);
}
- public void setWatcher(CoverViewWatcher listener)
+ public void setPlaybackService(IPlaybackService service)
{
- mListener = listener;
- }
-
- public Song getActiveSong()
- {
- return mSongs[1];
+ try {
+ mService = service;
+ mService.registerWatcher(mWatcher);
+ refreshSongs();
+ } catch (RemoteException e) {
+ }
}
private void drawText(Canvas canvas, String text, float left, float top, float width, float maxWidth, Paint paint)
@@ -159,18 +157,15 @@ public class CoverView extends View {
if (oldBitmap != null)
oldBitmap.recycle();
}
-
- public void setSongs(Song[] songs)
- {
- mSongs = songs;
- regenerateBitmaps();
- }
- public void setSong(int delta, Song song)
+ private void refreshSongs()
{
- int i = 1 + delta;
- mSongs[i] = song;
- createBitmap(i);
+ try {
+ mSongs = mService.getCurrentSongs();
+ regenerateBitmaps();
+ } catch (RemoteException e) {
+ Log.e("Tumult", "RemoteException", e);
+ }
}
public void regenerateBitmaps()
@@ -178,27 +173,58 @@ public class CoverView extends View {
if (getWidth() == 0 || getHeight() == 0)
return;
- for (int i = mSongs.length; --i != -1; )
+ for (int i = STORE_SIZE; --i != -1; )
createBitmap(i);
reset();
}
-
- public void shiftBackward()
+
+ public void nextCover()
{
- System.arraycopy(mSongs, 1, mSongs, 0, mSongs.length - 1);
- System.arraycopy(mBitmaps, 1, mBitmaps, 0, mBitmaps.length - 1);
- mSongs[mSongs.length - 1] = null;
- mBitmaps[mBitmaps.length - 1] = null;
- reset();
+ if (mService == null)
+ return;
+
+ try {
+ mService.nextSong();
+
+ System.arraycopy(mSongs, 1, mSongs, 0, STORE_SIZE - 1);
+ System.arraycopy(mBitmaps, 1, mBitmaps, 0, STORE_SIZE - 1);
+ mSongs[STORE_SIZE - 1] = null;
+ mBitmaps[STORE_SIZE - 1] = null;
+ reset();
+
+ mHandler.sendMessage(mHandler.obtainMessage(QUERY_SONG, 2, 0));
+ } catch (RemoteException e) {
+ }
}
- public void shiftForward()
+ public void previousCover()
{
- System.arraycopy(mSongs, 0, mSongs, 1, mSongs.length - 1);
- System.arraycopy(mBitmaps, 0, mBitmaps, 1, mBitmaps.length - 1);
- mSongs[0] = null;
- mBitmaps[0] = null;
- reset();
+ if (mService == null)
+ return;
+
+ try {
+ mService.previousSong();
+
+ System.arraycopy(mSongs, 0, mSongs, 1, STORE_SIZE - 1);
+ System.arraycopy(mBitmaps, 0, mBitmaps, 1, STORE_SIZE - 1);
+ mSongs[0] = null;
+ mBitmaps[0] = null;
+ reset();
+
+ mHandler.sendMessage(mHandler.obtainMessage(QUERY_SONG, 0, 0));
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void togglePlayback()
+ {
+ if (mService == null)
+ return;
+
+ try {
+ mService.togglePlayback();
+ } catch (RemoteException e) {
+ }
}
public void reset()
@@ -227,7 +253,7 @@ public class CoverView extends View {
canvas.drawColor(Color.BLACK);
- for (int x = 0, i = 0; i != mBitmaps.length; ++i, x += width) {
+ for (int x = 0, i = 0; i != STORE_SIZE; ++i, x += width) {
if (mBitmaps[i] != null && clip.intersects(x, 0, x + width, height)) {
int xOffset = (width - mBitmaps[i].getWidth()) / 2;
int yOffset = (height - mBitmaps[i].getHeight()) / 2;
@@ -274,7 +300,7 @@ public class CoverView extends View {
break;
case MotionEvent.ACTION_UP:
if (Math.abs(mStartX - x) + Math.abs(mStartY - ev.getY()) < 10) {
- mListener.clicked();
+ performClick();
} else {
VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000);
@@ -314,13 +340,46 @@ public class CoverView extends View {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
} else if (mTentativeCover != -1) {
- if (mListener != null) {
- if (mTentativeCover == 2)
- mListener.next();
- else if (mTentativeCover == 0)
- mListener.previous();
- }
+ if (mTentativeCover == 2)
+ nextCover();
+ else if (mTentativeCover == 0)
+ previousCover();
+
mTentativeCover = -1;
}
}
-}
+
+ private static final int REFRESH_SONGS = 0;
+ private static final int QUERY_SONG = 1;
+
+ private Handler mHandler = new Handler() {
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case REFRESH_SONGS:
+ refreshSongs();
+ break;
+ case QUERY_SONG:
+ try {
+ int i = message.arg1;
+ int delta = i - STORE_SIZE / 2;
+ mSongs[i] = mService.getSong(delta);
+ createBitmap(i);
+ } catch (RemoteException e) {
+ }
+ break;
+ }
+ }
+ };
+
+ private IMusicPlayerWatcher mWatcher = new IMusicPlayerWatcher.Stub() {
+ public void songChanged(Song playingSong)
+ {
+ if (!playingSong.equals(mSongs[STORE_SIZE / 2]))
+ mHandler.sendEmptyMessage(REFRESH_SONGS);
+ }
+
+ public void stateChanged(int oldState, int newState)
+ {
+ }
+ };
+}
\ No newline at end of file
diff --git a/src/org/kreed/tumult/MusicPlayer.java b/src/org/kreed/tumult/MusicPlayer.java
index fc25f693..6bf7c85c 100644
--- a/src/org/kreed/tumult/MusicPlayer.java
+++ b/src/org/kreed/tumult/MusicPlayer.java
@@ -115,9 +115,10 @@ public class MusicPlayer implements Runnable, MediaPlayer.OnCompletionListener,
private PlaybackService mService;
private RemoteCallbackList mWatchers;
-
- private boolean mHeadsetOnly = true;
-
+
+ private boolean mHeadsetOnly;
+ private boolean mUseRemotePlayer;
+
private Handler mHandler;
private MediaPlayer mMediaPlayer;
private Random mRandom;
@@ -149,6 +150,7 @@ public class MusicPlayer implements Runnable, MediaPlayer.OnCompletionListener,
private static final int HANDLE_PLAY = 7;
private static final int HANDLE_PAUSE = 8;
private static final int RETRIEVE_SONGS = 9;
+ private static final int REMOTE_PLAYER_PREF_CHANGED = 10;
private static final int ITEM_SONG = 0;
private static final int ITEM_RESET = 1;
@@ -220,6 +222,11 @@ public class MusicPlayer implements Runnable, MediaPlayer.OnCompletionListener,
case RETRIEVE_SONGS:
retrieveSongs();
break;
+ case REMOTE_PLAYER_PREF_CHANGED:
+ mUseRemotePlayer = message.arg1 == 1;
+ if (mState == STATE_PLAYING)
+ mService.startForegroundCompat(NOTIFICATION_ID, createNotfication());
+ break;
}
}
};
@@ -237,11 +244,12 @@ public class MusicPlayer implements Runnable, MediaPlayer.OnCompletionListener,
mMediaPlayer.setWakeMode(mService, PowerManager.PARTIAL_WAKE_LOCK);
mMediaPlayer.setOnCompletionListener(this);
retrieveSongs();
-
+
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(mService);
mHeadsetOnly = settings.getBoolean("headset_only", false);
+ mUseRemotePlayer = settings.getBoolean("remote_player", true);
settings.registerOnSharedPreferenceChangeListener(this);
-
+
setCurrentSong(1);
Looper.loop();
@@ -302,7 +310,7 @@ public class MusicPlayer implements Runnable, MediaPlayer.OnCompletionListener,
notification.contentView = views;
notification.icon = R.drawable.status_icon;
notification.flags |= Notification.FLAG_ONGOING_EVENT;
- Intent intent = new Intent(mService, NowPlayingActivity.class);
+ Intent intent = new Intent(mService, mUseRemotePlayer ? RemoteActivity.class : NowPlayingActivity.class);
notification.contentIntent = PendingIntent.getActivity(mService, 0, intent, 0);
return notification;
@@ -416,9 +424,15 @@ public class MusicPlayer implements Runnable, MediaPlayer.OnCompletionListener,
public void onSharedPreferenceChanged(SharedPreferences settings, String key)
{
- if ("headset_only".equals(key) && mHandler != null) {
+ if (mHandler == null)
+ return;
+
+ if ("headset_only".equals(key)) {
int arg = settings.getBoolean(key, false) ? 1 : 0;
mHandler.sendMessage(mHandler.obtainMessage(HEADSET_PREF_CHANGED, arg, 0));
+ } else if ("remote_player".equals(key)) {
+ int arg = settings.getBoolean(key, true) ? 1 : 0;
+ mHandler.sendMessage(mHandler.obtainMessage(REMOTE_PLAYER_PREF_CHANGED, arg, 0));
}
}
}
diff --git a/src/org/kreed/tumult/NowPlayingActivity.java b/src/org/kreed/tumult/NowPlayingActivity.java
index cb7cf117..0c109cb8 100644
--- a/src/org/kreed/tumult/NowPlayingActivity.java
+++ b/src/org/kreed/tumult/NowPlayingActivity.java
@@ -1,7 +1,5 @@
package org.kreed.tumult;
-import org.kreed.tumult.CoverView.CoverViewWatcher;
-
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
@@ -25,7 +23,7 @@ import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
-public class NowPlayingActivity extends Activity implements CoverViewWatcher, ServiceConnection, View.OnClickListener, SeekBar.OnSeekBarChangeListener, View.OnFocusChangeListener {
+public class NowPlayingActivity extends Activity implements ServiceConnection, View.OnClickListener, SeekBar.OnSeekBarChangeListener, View.OnFocusChangeListener {
private IPlaybackService mService;
private ViewGroup mLayout;
@@ -55,8 +53,8 @@ public class NowPlayingActivity extends Activity implements CoverViewWatcher, Se
setContentView(R.layout.nowplaying);
mCoverView = (CoverView)findViewById(R.id.cover_view);
- mCoverView.setWatcher(this);
-
+ mCoverView.setOnClickListener(this);
+
mLayout = (ViewGroup)mCoverView.getParent();
mControlsTop = findViewById(R.id.controls_top);
@@ -139,21 +137,12 @@ public class NowPlayingActivity extends Activity implements CoverViewWatcher, Se
unbindService(this);
}
- private void refreshSongs()
- {
- try {
- mCoverView.setSongs(mService.getCurrentSongs());
- } catch (RemoteException e) {
- Log.e("Tumult", "RemoteException", e);
- }
- }
-
public void onServiceConnected(ComponentName name, IBinder service)
{
mService = IPlaybackService.Stub.asInterface(service);
try {
mService.registerWatcher(mWatcher);
- refreshSongs();
+ mCoverView.setPlaybackService(mService);
setState(mService.getState());
mDuration = mService.getDuration();
} catch (RemoteException e) {
@@ -175,15 +164,6 @@ public class NowPlayingActivity extends Activity implements CoverViewWatcher, Se
mHandler.sendEmptyMessage(UPDATE_PROGRESS);
} catch (RemoteException e) {
}
-
- if (!playingSong.equals(mCoverView.getActiveSong())) {
- runOnUiThread(new Runnable() {
- public void run()
- {
- refreshSongs();
- }
- });
- }
}
public void stateChanged(final int oldState, final int newState)
@@ -197,34 +177,6 @@ public class NowPlayingActivity extends Activity implements CoverViewWatcher, Se
}
};
- public void next()
- {
- try {
- mService.nextSong();
- mCoverView.shiftBackward();
- mHandler.sendMessage(mHandler.obtainMessage(QUERY_SONG, 1, 0));
- } catch (RemoteException e) {
- }
- }
-
- public void previous()
- {
- try {
- mService.previousSong();
- mCoverView.shiftForward();
- mHandler.sendMessage(mHandler.obtainMessage(QUERY_SONG, -1, 0));
- } catch (RemoteException e) {
- }
- }
-
- private void togglePlayback()
- {
- try {
- mService.togglePlayback();
- } catch (RemoteException e) {
- }
- }
-
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
@@ -261,30 +213,13 @@ public class NowPlayingActivity extends Activity implements CoverViewWatcher, Se
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_ENTER:
- clicked();
+ onClick(mCoverView);
return true;
}
return false;
}
- public void clicked()
- {
- if (mControlsTop.getVisibility() == View.VISIBLE) {
- mControlsTop.setVisibility(View.GONE);
- if (mState == MusicPlayer.STATE_PLAYING)
- mControlsBottom.setVisibility(View.GONE);
- } else {
- mControlsTop.setVisibility(View.VISIBLE);
- mControlsBottom.setVisibility(View.VISIBLE);
-
- mPlayPauseButton.requestFocus();
-
- updateProgress();
- sendHideMessage();
- }
- }
-
private String stringForTime(int ms)
{
int seconds = ms / 1000;
@@ -331,18 +266,30 @@ public class NowPlayingActivity extends Activity implements CoverViewWatcher, Se
{
sendHideMessage();
- if (view == mNextButton) {
- next();
+ if (view == mCoverView) {
+ if (mControlsTop.getVisibility() == View.VISIBLE) {
+ mControlsTop.setVisibility(View.GONE);
+ if (mState == MusicPlayer.STATE_PLAYING)
+ mControlsBottom.setVisibility(View.GONE);
+ } else {
+ mControlsTop.setVisibility(View.VISIBLE);
+ mControlsBottom.setVisibility(View.VISIBLE);
+
+ mPlayPauseButton.requestFocus();
+
+ updateProgress();
+ }
+ } else if (view == mNextButton) {
+ mCoverView.nextCover();
} else if (view == mPreviousButton) {
- previous();
+ mCoverView.previousCover();
} else if (view == mPlayPauseButton) {
- togglePlayback();
+ mCoverView.togglePlayback();
}
}
private static final int HIDE = 0;
private static final int UPDATE_PROGRESS = 1;
- private static final int QUERY_SONG = 2;
private Handler mHandler = new Handler() {
public void handleMessage(Message message) {
@@ -355,12 +302,6 @@ public class NowPlayingActivity extends Activity implements CoverViewWatcher, Se
case UPDATE_PROGRESS:
updateProgress();
break;
- case QUERY_SONG:
- try {
- int delta = message.arg1;
- mCoverView.setSong(delta, mService.getSong(delta));
- } catch (RemoteException e) {
- }
}
}
};
diff --git a/src/org/kreed/tumult/RemoteActivity.java b/src/org/kreed/tumult/RemoteActivity.java
new file mode 100644
index 00000000..2d7e24e3
--- /dev/null
+++ b/src/org/kreed/tumult/RemoteActivity.java
@@ -0,0 +1,125 @@
+package org.kreed.tumult;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.view.View;
+import android.view.Window;
+import android.widget.ImageButton;
+
+public class RemoteActivity extends Activity implements ServiceConnection, View.OnClickListener {
+ private CoverView mCoverView;
+
+ private View mOpenButton;
+ private View mKillButton;
+ private View mPreviousButton;
+ private ImageButton mPlayPauseButton;
+ private View mNextButton;
+
+ @Override
+ public void onCreate(Bundle state)
+ {
+ super.onCreate(state);
+
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ setContentView(R.layout.remote_dialog);
+
+ mCoverView = (CoverView)findViewById(R.id.cover_view);
+
+ mOpenButton = findViewById(R.id.open_button);
+ mOpenButton.setOnClickListener(this);
+ mKillButton = findViewById(R.id.kill_button);
+ mKillButton.setOnClickListener(this);
+ mPreviousButton = findViewById(R.id.previous);
+ mPreviousButton.setOnClickListener(this);
+ mPlayPauseButton = (ImageButton)findViewById(R.id.play_pause);
+ mPlayPauseButton.setOnClickListener(this);
+ mNextButton = findViewById(R.id.next);
+ mNextButton.setOnClickListener(this);
+ }
+
+ @Override
+ public void onResume()
+ {
+ super.onResume();
+
+ reconnect();
+ }
+
+ private void reconnect()
+ {
+ Intent intent = new Intent(this, PlaybackService.class);
+ startService(intent);
+ bindService(intent, this, Context.BIND_AUTO_CREATE);
+ }
+
+ @Override
+ public void onPause()
+ {
+ super.onPause();
+
+ unbindService(this);
+ }
+
+ public void onServiceConnected(ComponentName name, IBinder binder)
+ {
+ IPlaybackService service = IPlaybackService.Stub.asInterface(binder);
+ mCoverView.setPlaybackService(service);
+ try {
+ service.registerWatcher(mWatcher);
+ setState(service.getState());
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName name)
+ {
+ reconnect();
+ }
+
+ public void onClick(View view)
+ {
+ if (view == mKillButton) {
+ stopService(new Intent(this, PlaybackService.class));
+ finish();
+ } else if (view == mOpenButton) {
+ startActivity(new Intent(this, NowPlayingActivity.class));
+ finish();
+ } else if (view == mNextButton) {
+ mCoverView.nextCover();
+ } else if (view == mPreviousButton) {
+ mCoverView.previousCover();
+ } else if (view == mPlayPauseButton) {
+ mCoverView.togglePlayback();
+ }
+ }
+
+ private void setState(int state)
+ {
+ if (state == MusicPlayer.STATE_NO_MEDIA)
+ finish();
+
+ mPlayPauseButton.setImageResource(state == MusicPlayer.STATE_PLAYING ? R.drawable.pause : R.drawable.play);
+ }
+
+ private IMusicPlayerWatcher mWatcher = new IMusicPlayerWatcher.Stub() {
+ public void songChanged(Song playingSong)
+ {
+ }
+
+ public void stateChanged(final int oldState, final int newState)
+ {
+ runOnUiThread(new Runnable() {
+ public void run()
+ {
+ setState(newState);
+ }
+ });
+ }
+ };
+}
\ No newline at end of file
diff --git a/src/org/kreed/tumult/RemoteLayout.java b/src/org/kreed/tumult/RemoteLayout.java
new file mode 100644
index 00000000..39804dbb
--- /dev/null
+++ b/src/org/kreed/tumult/RemoteLayout.java
@@ -0,0 +1,76 @@
+package org.kreed.tumult;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+/*
+ * RemoteLayout acts like a very simple vertical LinearLayout with special
+ * case: all CoverViews placed will be made square at all costs.
+ */
+
+public class RemoteLayout extends ViewGroup {
+ private int mCoverSize;
+
+ public RemoteLayout(Context context, AttributeSet attrs)
+ {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
+ {
+ int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
+
+ int measuredHeight = 0;
+ int measuredWidth = 0;
+
+ View coverView = null;
+ for (int i = getChildCount(); --i != -1; ) {
+ View view = getChildAt(i);
+ if (view instanceof CoverView) {
+ coverView = view;
+ } else {
+ int spec = MeasureSpec.makeMeasureSpec(maxHeight - measuredHeight, MeasureSpec.AT_MOST);
+ view.measure(widthMeasureSpec, spec);
+ measuredHeight += view.getMeasuredHeight();
+ if (view.getMeasuredWidth() > measuredWidth)
+ measuredWidth = view.getMeasuredWidth();
+ }
+ }
+
+ if (coverView != null) {
+ if (measuredHeight + measuredWidth > maxHeight) {
+ mCoverSize = maxHeight - measuredHeight;
+ measuredHeight = maxHeight;
+ } else {
+ mCoverSize = measuredWidth;
+ measuredHeight += measuredWidth;
+ }
+ }
+
+ setMeasuredDimension(measuredWidth, measuredHeight);
+ }
+
+ @Override
+ protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4)
+ {
+ int layoutWidth = getMeasuredWidth();
+ int top = 0;
+
+ for (int i = 0, end = getChildCount(); i != end; ++i) {
+ View view = getChildAt(i);
+ if (view instanceof CoverView) {
+ view.layout(0, top, layoutWidth, top + mCoverSize);
+ top += mCoverSize;
+ } else {
+ int height = view.getMeasuredHeight();
+ int width = view.getMeasuredWidth();
+ int left = (layoutWidth - width) / 2;
+ view.layout(left, top, layoutWidth - left, top + height);
+ top += height;
+ }
+ }
+ }
+}
\ No newline at end of file