Remove windows line-endings
This commit is contained in:
parent
41dc843dcc
commit
3220ee32be
14
.classpath
14
.classpath
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
66
.project
66
.project
@ -1,33 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>Tumult</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>Tumult</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
|
@ -1,5 +1,5 @@
|
||||
#Thu Dec 24 11:30:20 CST 2009
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
|
||||
org.eclipse.jdt.core.compiler.compliance=1.5
|
||||
org.eclipse.jdt.core.compiler.source=1.5
|
||||
#Thu Dec 24 11:30:20 CST 2009
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
|
||||
org.eclipse.jdt.core.compiler.compliance=1.5
|
||||
org.eclipse.jdt.core.compiler.source=1.5
|
||||
|
@ -1,307 +1,307 @@
|
||||
package org.kreed.tumult;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Region;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.View;
|
||||
import android.widget.Scroller;
|
||||
|
||||
public class CoverView extends View {
|
||||
private static final int SNAP_VELOCITY = 1000;
|
||||
|
||||
private Scroller mScroller;
|
||||
private VelocityTracker mVelocityTracker;
|
||||
private float mLastMotionX;
|
||||
private float mStartX;
|
||||
private float mStartY;
|
||||
|
||||
private CoverViewWatcher mListener;
|
||||
|
||||
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);
|
||||
|
||||
mScroller = new Scroller(context);
|
||||
}
|
||||
|
||||
public void setWatcher(CoverViewWatcher listener)
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
private RectF scale(Bitmap bitmap, int maxWidth, int maxHeight)
|
||||
{
|
||||
float bitmapWidth = bitmap.getWidth();
|
||||
float bitmapHeight = bitmap.getHeight();
|
||||
|
||||
float drawableAspectRatio = bitmapHeight / bitmapWidth;
|
||||
float viewAspectRatio = (float) maxHeight / maxWidth;
|
||||
float scale = drawableAspectRatio > viewAspectRatio ? maxHeight / bitmapWidth
|
||||
: maxWidth / bitmapHeight;
|
||||
|
||||
bitmapWidth *= scale;
|
||||
bitmapHeight *= scale;
|
||||
|
||||
float left = (maxWidth - bitmapWidth) / 2;
|
||||
float top = (maxHeight - bitmapHeight) / 2;
|
||||
|
||||
return new RectF(left, top, maxWidth - left, maxHeight - top);
|
||||
}
|
||||
|
||||
private void drawText(Canvas canvas, String text, float left, float top, float width, float maxWidth, Paint paint)
|
||||
{
|
||||
float offset = Math.max(0, maxWidth - width) / 2;
|
||||
canvas.clipRect(left, top, left + maxWidth, top + paint.getTextSize() * 2, Region.Op.REPLACE);
|
||||
canvas.drawText(text, left + offset, top - paint.ascent(), paint);
|
||||
}
|
||||
|
||||
private void createBitmap(int i)
|
||||
{
|
||||
Song song = mSongs[i];
|
||||
Bitmap bitmap = null;
|
||||
|
||||
if (song != null) {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
|
||||
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
Paint paint = new Paint();
|
||||
|
||||
Bitmap cover = song.coverPath == null ? null : BitmapFactory.decodeFile(song.coverPath);
|
||||
if (cover != null) {
|
||||
RectF dest = scale(cover, width, height);
|
||||
canvas.drawBitmap(cover, new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()), dest, paint);
|
||||
cover.recycle();
|
||||
cover = null;
|
||||
}
|
||||
|
||||
paint.setAntiAlias(true);
|
||||
|
||||
String title = song.title == null ? "" : song.title;
|
||||
String album = song.album == null ? "" : song.album;
|
||||
String artist = song.artist == null ? "" : song.artist;
|
||||
|
||||
float titleSize = 20;
|
||||
float subSize = 14;
|
||||
float padding = 10;
|
||||
|
||||
paint.setTextSize(titleSize);
|
||||
float titleWidth = paint.measureText(title);
|
||||
paint.setTextSize(subSize);
|
||||
float albumWidth = paint.measureText(album);
|
||||
float artistWidth = paint.measureText(artist);
|
||||
|
||||
float boxWidth = Math.max(titleWidth, Math.max(artistWidth, albumWidth)) + padding * 2;
|
||||
float boxHeight = titleSize + subSize * 2 + padding * 4;
|
||||
|
||||
boxWidth = Math.min(boxWidth, width);
|
||||
boxHeight = Math.min(boxHeight, height);
|
||||
|
||||
paint.setARGB(150, 0, 0, 0);
|
||||
float left = (width - boxWidth) / 2;
|
||||
float top = (height - boxHeight) / 2;
|
||||
float right = (width + boxWidth) / 2;
|
||||
float bottom = (height + boxHeight) / 2;
|
||||
canvas.drawRect(left, top, right, bottom, paint);
|
||||
|
||||
float maxWidth = boxWidth - padding * 2;
|
||||
paint.setARGB(255, 255, 255, 255);
|
||||
top += padding;
|
||||
left += padding;
|
||||
|
||||
paint.setTextSize(titleSize);
|
||||
drawText(canvas, title, left, top, titleWidth, maxWidth, paint);
|
||||
top += titleSize + padding;
|
||||
|
||||
paint.setTextSize(subSize);
|
||||
drawText(canvas, album, left, top, albumWidth, maxWidth, paint);
|
||||
top += subSize + padding;
|
||||
|
||||
drawText(canvas, artist, left, top, artistWidth, maxWidth, paint);
|
||||
}
|
||||
|
||||
Bitmap oldBitmap = mBitmaps[i];
|
||||
mBitmaps[i] = bitmap;
|
||||
if (oldBitmap != null)
|
||||
oldBitmap.recycle();
|
||||
}
|
||||
|
||||
public void setSongs(Song[] songs)
|
||||
{
|
||||
mSongs = songs;
|
||||
regenerateBitmaps();
|
||||
}
|
||||
|
||||
public void regenerateBitmaps()
|
||||
{
|
||||
if (getWidth() == 0 || getHeight() == 0)
|
||||
return;
|
||||
|
||||
for (int i = mSongs.length; --i != -1; )
|
||||
createBitmap(i);
|
||||
reset();
|
||||
}
|
||||
|
||||
public void setForwardSong(Song song)
|
||||
{
|
||||
if (mSongs[mSongs.length - 1] != null) {
|
||||
System.arraycopy(mSongs, 1, mSongs, 0, mSongs.length - 1);
|
||||
System.arraycopy(mBitmaps, 1, mBitmaps, 0, mBitmaps.length - 1);
|
||||
mBitmaps[mBitmaps.length - 1] = null;
|
||||
reset();
|
||||
}
|
||||
|
||||
mSongs[mSongs.length - 1] = song;
|
||||
createBitmap(mSongs.length - 1);
|
||||
}
|
||||
|
||||
public void setBackwardSong(Song song)
|
||||
{
|
||||
if (mSongs[0] != null) {
|
||||
System.arraycopy(mSongs, 0, mSongs, 1, mSongs.length - 1);
|
||||
System.arraycopy(mBitmaps, 0, mBitmaps, 1, mBitmaps.length - 1);
|
||||
mBitmaps[0] = null;
|
||||
reset();
|
||||
}
|
||||
|
||||
mSongs[0] = song;
|
||||
createBitmap(0);
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
if (!mScroller.isFinished())
|
||||
mScroller.abortAnimation();
|
||||
scrollTo(getWidth(), 0);
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh)
|
||||
{
|
||||
regenerateBitmaps();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas)
|
||||
{
|
||||
super.onDraw(canvas);
|
||||
|
||||
Rect clip = canvas.getClipBounds();
|
||||
Paint paint = new Paint();
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
|
||||
for (int x = 0, i = 0; i != mBitmaps.length; ++i, x += width)
|
||||
if (mBitmaps[i] != null && clip.intersects(x, 0, x + width, height))
|
||||
canvas.drawBitmap(mBitmaps[i], x, 0, paint);
|
||||
paint = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev)
|
||||
{
|
||||
// based on code from com.android.launcher.Workspace
|
||||
if (mVelocityTracker == null)
|
||||
mVelocityTracker = VelocityTracker.obtain();
|
||||
mVelocityTracker.addMovement(ev);
|
||||
|
||||
float x = ev.getX();
|
||||
int scrollX = getScrollX();
|
||||
int width = getWidth();
|
||||
|
||||
switch (ev.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
if (!mScroller.isFinished())
|
||||
mScroller.abortAnimation();
|
||||
|
||||
mStartX = x;
|
||||
mStartY = ev.getY();
|
||||
mLastMotionX = x;
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
int deltaX = (int) (mLastMotionX - x);
|
||||
mLastMotionX = x;
|
||||
|
||||
if (deltaX < 0) {
|
||||
int availableToScroll = scrollX - (mBitmaps[0] == null ? width : 0);
|
||||
if (availableToScroll > 0)
|
||||
scrollBy(Math.max(-availableToScroll, deltaX), 0);
|
||||
} else if (deltaX > 0) {
|
||||
int availableToScroll = getWidth() * 2 - scrollX;
|
||||
if (availableToScroll > 0)
|
||||
scrollBy(Math.min(availableToScroll, deltaX), 0);
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
if (Math.abs(mStartX - x) + Math.abs(mStartY - ev.getY()) < 10) {
|
||||
mListener.clicked();
|
||||
} else {
|
||||
VelocityTracker velocityTracker = mVelocityTracker;
|
||||
velocityTracker.computeCurrentVelocity(1000);
|
||||
int velocity = (int) velocityTracker.getXVelocity();
|
||||
|
||||
int min = mBitmaps[0] == null ? 1 : 0;
|
||||
int max = 2;
|
||||
int nearestCover = (scrollX + width / 2) / width;
|
||||
int whichCover = Math.max(min, Math.min(nearestCover, max));
|
||||
|
||||
if (velocity > SNAP_VELOCITY && whichCover != min)
|
||||
--whichCover;
|
||||
else if (velocity < -SNAP_VELOCITY && whichCover != max)
|
||||
++whichCover;
|
||||
|
||||
int newX = whichCover * width;
|
||||
int delta = newX - scrollX;
|
||||
mScroller.startScroll(scrollX, 0, delta, 0, Math.abs(delta) * 2);
|
||||
mTentativeCover = whichCover;
|
||||
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
if (mVelocityTracker != null) {
|
||||
mVelocityTracker.recycle();
|
||||
mVelocityTracker = null;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void computeScroll()
|
||||
{
|
||||
if (mScroller.computeScrollOffset()) {
|
||||
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
|
||||
postInvalidate();
|
||||
} else if (mTentativeCover != -1) {
|
||||
if (mListener != null) {
|
||||
if (mTentativeCover == 2)
|
||||
mListener.next();
|
||||
else if (mTentativeCover == 0)
|
||||
mListener.previous();
|
||||
}
|
||||
mTentativeCover = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
package org.kreed.tumult;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Region;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.View;
|
||||
import android.widget.Scroller;
|
||||
|
||||
public class CoverView extends View {
|
||||
private static final int SNAP_VELOCITY = 1000;
|
||||
|
||||
private Scroller mScroller;
|
||||
private VelocityTracker mVelocityTracker;
|
||||
private float mLastMotionX;
|
||||
private float mStartX;
|
||||
private float mStartY;
|
||||
|
||||
private CoverViewWatcher mListener;
|
||||
|
||||
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);
|
||||
|
||||
mScroller = new Scroller(context);
|
||||
}
|
||||
|
||||
public void setWatcher(CoverViewWatcher listener)
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
private RectF scale(Bitmap bitmap, int maxWidth, int maxHeight)
|
||||
{
|
||||
float bitmapWidth = bitmap.getWidth();
|
||||
float bitmapHeight = bitmap.getHeight();
|
||||
|
||||
float drawableAspectRatio = bitmapHeight / bitmapWidth;
|
||||
float viewAspectRatio = (float) maxHeight / maxWidth;
|
||||
float scale = drawableAspectRatio > viewAspectRatio ? maxHeight / bitmapWidth
|
||||
: maxWidth / bitmapHeight;
|
||||
|
||||
bitmapWidth *= scale;
|
||||
bitmapHeight *= scale;
|
||||
|
||||
float left = (maxWidth - bitmapWidth) / 2;
|
||||
float top = (maxHeight - bitmapHeight) / 2;
|
||||
|
||||
return new RectF(left, top, maxWidth - left, maxHeight - top);
|
||||
}
|
||||
|
||||
private void drawText(Canvas canvas, String text, float left, float top, float width, float maxWidth, Paint paint)
|
||||
{
|
||||
float offset = Math.max(0, maxWidth - width) / 2;
|
||||
canvas.clipRect(left, top, left + maxWidth, top + paint.getTextSize() * 2, Region.Op.REPLACE);
|
||||
canvas.drawText(text, left + offset, top - paint.ascent(), paint);
|
||||
}
|
||||
|
||||
private void createBitmap(int i)
|
||||
{
|
||||
Song song = mSongs[i];
|
||||
Bitmap bitmap = null;
|
||||
|
||||
if (song != null) {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
|
||||
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
Paint paint = new Paint();
|
||||
|
||||
Bitmap cover = song.coverPath == null ? null : BitmapFactory.decodeFile(song.coverPath);
|
||||
if (cover != null) {
|
||||
RectF dest = scale(cover, width, height);
|
||||
canvas.drawBitmap(cover, new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()), dest, paint);
|
||||
cover.recycle();
|
||||
cover = null;
|
||||
}
|
||||
|
||||
paint.setAntiAlias(true);
|
||||
|
||||
String title = song.title == null ? "" : song.title;
|
||||
String album = song.album == null ? "" : song.album;
|
||||
String artist = song.artist == null ? "" : song.artist;
|
||||
|
||||
float titleSize = 20;
|
||||
float subSize = 14;
|
||||
float padding = 10;
|
||||
|
||||
paint.setTextSize(titleSize);
|
||||
float titleWidth = paint.measureText(title);
|
||||
paint.setTextSize(subSize);
|
||||
float albumWidth = paint.measureText(album);
|
||||
float artistWidth = paint.measureText(artist);
|
||||
|
||||
float boxWidth = Math.max(titleWidth, Math.max(artistWidth, albumWidth)) + padding * 2;
|
||||
float boxHeight = titleSize + subSize * 2 + padding * 4;
|
||||
|
||||
boxWidth = Math.min(boxWidth, width);
|
||||
boxHeight = Math.min(boxHeight, height);
|
||||
|
||||
paint.setARGB(150, 0, 0, 0);
|
||||
float left = (width - boxWidth) / 2;
|
||||
float top = (height - boxHeight) / 2;
|
||||
float right = (width + boxWidth) / 2;
|
||||
float bottom = (height + boxHeight) / 2;
|
||||
canvas.drawRect(left, top, right, bottom, paint);
|
||||
|
||||
float maxWidth = boxWidth - padding * 2;
|
||||
paint.setARGB(255, 255, 255, 255);
|
||||
top += padding;
|
||||
left += padding;
|
||||
|
||||
paint.setTextSize(titleSize);
|
||||
drawText(canvas, title, left, top, titleWidth, maxWidth, paint);
|
||||
top += titleSize + padding;
|
||||
|
||||
paint.setTextSize(subSize);
|
||||
drawText(canvas, album, left, top, albumWidth, maxWidth, paint);
|
||||
top += subSize + padding;
|
||||
|
||||
drawText(canvas, artist, left, top, artistWidth, maxWidth, paint);
|
||||
}
|
||||
|
||||
Bitmap oldBitmap = mBitmaps[i];
|
||||
mBitmaps[i] = bitmap;
|
||||
if (oldBitmap != null)
|
||||
oldBitmap.recycle();
|
||||
}
|
||||
|
||||
public void setSongs(Song[] songs)
|
||||
{
|
||||
mSongs = songs;
|
||||
regenerateBitmaps();
|
||||
}
|
||||
|
||||
public void regenerateBitmaps()
|
||||
{
|
||||
if (getWidth() == 0 || getHeight() == 0)
|
||||
return;
|
||||
|
||||
for (int i = mSongs.length; --i != -1; )
|
||||
createBitmap(i);
|
||||
reset();
|
||||
}
|
||||
|
||||
public void setForwardSong(Song song)
|
||||
{
|
||||
if (mSongs[mSongs.length - 1] != null) {
|
||||
System.arraycopy(mSongs, 1, mSongs, 0, mSongs.length - 1);
|
||||
System.arraycopy(mBitmaps, 1, mBitmaps, 0, mBitmaps.length - 1);
|
||||
mBitmaps[mBitmaps.length - 1] = null;
|
||||
reset();
|
||||
}
|
||||
|
||||
mSongs[mSongs.length - 1] = song;
|
||||
createBitmap(mSongs.length - 1);
|
||||
}
|
||||
|
||||
public void setBackwardSong(Song song)
|
||||
{
|
||||
if (mSongs[0] != null) {
|
||||
System.arraycopy(mSongs, 0, mSongs, 1, mSongs.length - 1);
|
||||
System.arraycopy(mBitmaps, 0, mBitmaps, 1, mBitmaps.length - 1);
|
||||
mBitmaps[0] = null;
|
||||
reset();
|
||||
}
|
||||
|
||||
mSongs[0] = song;
|
||||
createBitmap(0);
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
if (!mScroller.isFinished())
|
||||
mScroller.abortAnimation();
|
||||
scrollTo(getWidth(), 0);
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh)
|
||||
{
|
||||
regenerateBitmaps();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas)
|
||||
{
|
||||
super.onDraw(canvas);
|
||||
|
||||
Rect clip = canvas.getClipBounds();
|
||||
Paint paint = new Paint();
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
|
||||
for (int x = 0, i = 0; i != mBitmaps.length; ++i, x += width)
|
||||
if (mBitmaps[i] != null && clip.intersects(x, 0, x + width, height))
|
||||
canvas.drawBitmap(mBitmaps[i], x, 0, paint);
|
||||
paint = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev)
|
||||
{
|
||||
// based on code from com.android.launcher.Workspace
|
||||
if (mVelocityTracker == null)
|
||||
mVelocityTracker = VelocityTracker.obtain();
|
||||
mVelocityTracker.addMovement(ev);
|
||||
|
||||
float x = ev.getX();
|
||||
int scrollX = getScrollX();
|
||||
int width = getWidth();
|
||||
|
||||
switch (ev.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
if (!mScroller.isFinished())
|
||||
mScroller.abortAnimation();
|
||||
|
||||
mStartX = x;
|
||||
mStartY = ev.getY();
|
||||
mLastMotionX = x;
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
int deltaX = (int) (mLastMotionX - x);
|
||||
mLastMotionX = x;
|
||||
|
||||
if (deltaX < 0) {
|
||||
int availableToScroll = scrollX - (mBitmaps[0] == null ? width : 0);
|
||||
if (availableToScroll > 0)
|
||||
scrollBy(Math.max(-availableToScroll, deltaX), 0);
|
||||
} else if (deltaX > 0) {
|
||||
int availableToScroll = getWidth() * 2 - scrollX;
|
||||
if (availableToScroll > 0)
|
||||
scrollBy(Math.min(availableToScroll, deltaX), 0);
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
if (Math.abs(mStartX - x) + Math.abs(mStartY - ev.getY()) < 10) {
|
||||
mListener.clicked();
|
||||
} else {
|
||||
VelocityTracker velocityTracker = mVelocityTracker;
|
||||
velocityTracker.computeCurrentVelocity(1000);
|
||||
int velocity = (int) velocityTracker.getXVelocity();
|
||||
|
||||
int min = mBitmaps[0] == null ? 1 : 0;
|
||||
int max = 2;
|
||||
int nearestCover = (scrollX + width / 2) / width;
|
||||
int whichCover = Math.max(min, Math.min(nearestCover, max));
|
||||
|
||||
if (velocity > SNAP_VELOCITY && whichCover != min)
|
||||
--whichCover;
|
||||
else if (velocity < -SNAP_VELOCITY && whichCover != max)
|
||||
++whichCover;
|
||||
|
||||
int newX = whichCover * width;
|
||||
int delta = newX - scrollX;
|
||||
mScroller.startScroll(scrollX, 0, delta, 0, Math.abs(delta) * 2);
|
||||
mTentativeCover = whichCover;
|
||||
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
if (mVelocityTracker != null) {
|
||||
mVelocityTracker.recycle();
|
||||
mVelocityTracker = null;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void computeScroll()
|
||||
{
|
||||
if (mScroller.computeScrollOffset()) {
|
||||
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
|
||||
postInvalidate();
|
||||
} else if (mTentativeCover != -1) {
|
||||
if (mListener != null) {
|
||||
if (mTentativeCover == 2)
|
||||
mListener.next();
|
||||
else if (mTentativeCover == 0)
|
||||
mListener.previous();
|
||||
}
|
||||
mTentativeCover = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
package org.kreed.tumult;
|
||||
|
||||
import org.kreed.tumult.Song;
|
||||
|
||||
oneway interface IMusicPlayerWatcher {
|
||||
void previousSong(in Song playingSong, in Song nextForwardSong);
|
||||
void nextSong(in Song playingSong, in Song nextBackwardSong);
|
||||
void stateChanged(in int oldState, in int newState);
|
||||
void mediaLengthChanged(in long startTime, in int duration);
|
||||
package org.kreed.tumult;
|
||||
|
||||
import org.kreed.tumult.Song;
|
||||
|
||||
oneway interface IMusicPlayerWatcher {
|
||||
void previousSong(in Song playingSong, in Song nextForwardSong);
|
||||
void nextSong(in Song playingSong, in Song nextBackwardSong);
|
||||
void stateChanged(in int oldState, in int newState);
|
||||
void mediaLengthChanged(in long startTime, in int duration);
|
||||
}
|
@ -1,18 +1,18 @@
|
||||
package org.kreed.tumult;
|
||||
|
||||
import org.kreed.tumult.Song;
|
||||
import org.kreed.tumult.IMusicPlayerWatcher;
|
||||
|
||||
interface IPlaybackService {
|
||||
void registerWatcher(IMusicPlayerWatcher watcher);
|
||||
|
||||
Song[] getCurrentSongs();
|
||||
int getState();
|
||||
long getStartTime();
|
||||
int getDuration();
|
||||
|
||||
void previousSong();
|
||||
void togglePlayback();
|
||||
void nextSong();
|
||||
void seekToProgress(int progress);
|
||||
package org.kreed.tumult;
|
||||
|
||||
import org.kreed.tumult.Song;
|
||||
import org.kreed.tumult.IMusicPlayerWatcher;
|
||||
|
||||
interface IPlaybackService {
|
||||
void registerWatcher(IMusicPlayerWatcher watcher);
|
||||
|
||||
Song[] getCurrentSongs();
|
||||
int getState();
|
||||
long getStartTime();
|
||||
int getDuration();
|
||||
|
||||
void previousSong();
|
||||
void togglePlayback();
|
||||
void nextSong();
|
||||
void seekToProgress(int progress);
|
||||
}
|
@ -1,430 +1,430 @@
|
||||
package org.kreed.tumult;
|
||||
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.util.Log;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Random;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.PowerManager;
|
||||
import android.os.RemoteCallbackList;
|
||||
import android.os.RemoteException;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
public class MusicPlayer implements Runnable, MediaPlayer.OnCompletionListener, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private static final int NOTIFICATION_ID = 2;
|
||||
|
||||
public static final int STATE_NORMAL = 0;
|
||||
public static final int STATE_NO_MEDIA = 1;
|
||||
public static final int STATE_PLAYING = 2;
|
||||
|
||||
public IPlaybackService.Stub mBinder = new IPlaybackService.Stub() {
|
||||
public Song[] getCurrentSongs()
|
||||
{
|
||||
return new Song[] { getSong(-1), getSong(0), getSong(1) };
|
||||
}
|
||||
|
||||
public int getState()
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
public long getStartTime()
|
||||
{
|
||||
if (mMediaPlayer == null)
|
||||
return 0;
|
||||
return MusicPlayer.this.getStartTime();
|
||||
}
|
||||
|
||||
public int getDuration()
|
||||
{
|
||||
if (mMediaPlayer == null)
|
||||
return 0;
|
||||
return mMediaPlayer.getDuration();
|
||||
}
|
||||
|
||||
public void nextSong()
|
||||
{
|
||||
if (mHandler == null)
|
||||
return;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(SET_SONG, 1, 0));
|
||||
}
|
||||
|
||||
public void previousSong()
|
||||
{
|
||||
if (mHandler == null)
|
||||
return;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(SET_SONG, -1, 0));
|
||||
}
|
||||
|
||||
public void togglePlayback()
|
||||
{
|
||||
if (mHandler == null)
|
||||
return;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(PLAY_PAUSE, 0, 0));
|
||||
}
|
||||
|
||||
public void registerWatcher(IMusicPlayerWatcher watcher)
|
||||
{
|
||||
if (watcher != null)
|
||||
mWatchers.register(watcher);
|
||||
}
|
||||
|
||||
public void seekToProgress(int progress)
|
||||
{
|
||||
if (mMediaPlayer == null || !mMediaPlayer.isPlaying())
|
||||
return;
|
||||
|
||||
long position = (long)mMediaPlayer.getDuration() * progress / 1000;
|
||||
mMediaPlayer.seekTo((int)position);
|
||||
mediaLengthChanged();
|
||||
}
|
||||
};
|
||||
|
||||
public void queueSong(int songId)
|
||||
{
|
||||
if (mHandler == null)
|
||||
return;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(QUEUE_ITEM, ITEM_SONG, songId));
|
||||
}
|
||||
|
||||
public void stopQueueing()
|
||||
{
|
||||
if (mHandler == null)
|
||||
return;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(QUEUE_ITEM, ITEM_RESET, 0));
|
||||
}
|
||||
|
||||
private PlaybackService mService;
|
||||
private RemoteCallbackList<IMusicPlayerWatcher> mWatchers;
|
||||
|
||||
private boolean mHeadsetOnly = true;
|
||||
|
||||
private Handler mHandler;
|
||||
private MediaPlayer mMediaPlayer;
|
||||
private Random mRandom;
|
||||
private PowerManager.WakeLock mWakeLock;
|
||||
|
||||
private int[] mSongs;
|
||||
private ArrayList<Song> mSongTimeline;
|
||||
private int mCurrentSong = -1;
|
||||
private int mQueuePos = 0;
|
||||
private boolean mPlugged = true;
|
||||
private int mState = STATE_NORMAL;
|
||||
|
||||
public MusicPlayer(PlaybackService service)
|
||||
{
|
||||
mService = service;
|
||||
mWatchers = new RemoteCallbackList<IMusicPlayerWatcher>();
|
||||
mSongTimeline = new ArrayList<Song>();
|
||||
|
||||
new Thread(this).start();
|
||||
}
|
||||
|
||||
private static final int SET_SONG = 0;
|
||||
private static final int PLAY_PAUSE = 1;
|
||||
private static final int HEADSET_PLUGGED = 2;
|
||||
private static final int HEADSET_PREF_CHANGED = 3;
|
||||
private static final int QUEUE_ITEM = 4;
|
||||
private static final int TRACK_CHANGED = 5;
|
||||
private static final int RELEASE_WAKE_LOCK = 6;
|
||||
|
||||
private static final int ITEM_SONG = 0;
|
||||
private static final int ITEM_RESET = 1;
|
||||
|
||||
public void run()
|
||||
{
|
||||
Looper.prepare();
|
||||
|
||||
mMediaPlayer = new MediaPlayer();
|
||||
mRandom = new Random();
|
||||
|
||||
mHandler = new Handler() {
|
||||
public void handleMessage(Message message)
|
||||
{
|
||||
switch (message.what) {
|
||||
case SET_SONG:
|
||||
setCurrentSong(message.arg1);
|
||||
break;
|
||||
case PLAY_PAUSE:
|
||||
if (mCurrentSong == -1) {
|
||||
setCurrentSong(+1);
|
||||
return;
|
||||
}
|
||||
|
||||
setPlaying(!mMediaPlayer.isPlaying());
|
||||
break;
|
||||
case HEADSET_PLUGGED:
|
||||
boolean plugged = message.arg1 == 1;
|
||||
if (plugged != mPlugged) {
|
||||
mPlugged = plugged;
|
||||
if (mCurrentSong == -1 || mPlugged == mMediaPlayer.isPlaying())
|
||||
return;
|
||||
setPlaying(mPlugged);
|
||||
}
|
||||
break;
|
||||
case HEADSET_PREF_CHANGED:
|
||||
mHeadsetOnly = message.arg1 == 1;
|
||||
if (mHeadsetOnly && !mPlugged && mMediaPlayer.isPlaying())
|
||||
pause();
|
||||
break;
|
||||
case QUEUE_ITEM:
|
||||
switch (message.arg1) {
|
||||
case ITEM_SONG:
|
||||
int i = mCurrentSong + 1 + mQueuePos++;
|
||||
Song song = new Song(message.arg2);
|
||||
Toast.makeText(Tumult.getContext(), "Enqueued " + song.title, Toast.LENGTH_SHORT).show();
|
||||
|
||||
if (i < mSongTimeline.size())
|
||||
mSongTimeline.set(i, song);
|
||||
else
|
||||
mSongTimeline.add(song);
|
||||
break;
|
||||
case ITEM_RESET:
|
||||
mQueuePos = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case TRACK_CHANGED:
|
||||
setCurrentSong(+1);
|
||||
break;
|
||||
case RELEASE_WAKE_LOCK:
|
||||
if (mWakeLock.isHeld())
|
||||
mWakeLock.release();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mService.registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
|
||||
|
||||
PowerManager powerManager = (PowerManager)mService.getSystemService(Context.POWER_SERVICE);
|
||||
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "TumultSongChangeLock");
|
||||
|
||||
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
||||
mMediaPlayer.setWakeMode(mService, PowerManager.PARTIAL_WAKE_LOCK);
|
||||
mMediaPlayer.setOnCompletionListener(this);
|
||||
retrieveSongs();
|
||||
|
||||
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(mService);
|
||||
mHeadsetOnly = settings.getBoolean("headset_only", false);
|
||||
settings.registerOnSharedPreferenceChangeListener(this);
|
||||
|
||||
mHandler.post(new Runnable() {
|
||||
public void run()
|
||||
{
|
||||
setCurrentSong(1);
|
||||
}
|
||||
});
|
||||
|
||||
Looper.loop();
|
||||
}
|
||||
|
||||
public void release()
|
||||
{
|
||||
if (mMediaPlayer != null) {
|
||||
pause();
|
||||
mMediaPlayer.release();
|
||||
mMediaPlayer = null;
|
||||
}
|
||||
|
||||
if (mWakeLock != null && mWakeLock.isHeld())
|
||||
mWakeLock.release();
|
||||
}
|
||||
|
||||
public void setState(int state)
|
||||
{
|
||||
if (mState == state)
|
||||
return;
|
||||
|
||||
int oldState = mState;
|
||||
mState = state;
|
||||
|
||||
int i = mWatchers.beginBroadcast();
|
||||
while (--i != -1) {
|
||||
try {
|
||||
mWatchers.getBroadcastItem(i).stateChanged(oldState, mState);
|
||||
} catch (RemoteException e) {
|
||||
// Null elements will be removed automatically
|
||||
}
|
||||
}
|
||||
mWatchers.finishBroadcast();
|
||||
}
|
||||
|
||||
private void retrieveSongs()
|
||||
{
|
||||
mSongs = Song.getAllSongs();
|
||||
if (mSongs == null && mState == STATE_NORMAL)
|
||||
setState(STATE_NO_MEDIA);
|
||||
}
|
||||
|
||||
private void play()
|
||||
{
|
||||
if (mHeadsetOnly && !mPlugged)
|
||||
return;
|
||||
|
||||
mMediaPlayer.start();
|
||||
|
||||
Song song = getSong(0);
|
||||
|
||||
RemoteViews views = new RemoteViews(mService.getPackageName(), R.layout.statusbar);
|
||||
views.setImageViewResource(R.id.icon, R.drawable.status_icon);
|
||||
views.setTextViewText(R.id.title, song.title);
|
||||
views.setTextViewText(R.id.artist, song.artist);
|
||||
|
||||
Notification notification = new Notification();
|
||||
notification.contentView = views;
|
||||
notification.icon = R.drawable.status_icon;
|
||||
notification.flags |= Notification.FLAG_ONGOING_EVENT;
|
||||
Intent intent = new Intent(mService, NowPlayingActivity.class);
|
||||
notification.contentIntent = PendingIntent.getActivity(mService, 0, intent, 0);
|
||||
|
||||
mService.startForegroundCompat(NOTIFICATION_ID, notification);
|
||||
|
||||
setState(STATE_PLAYING);
|
||||
}
|
||||
|
||||
private void pause()
|
||||
{
|
||||
mMediaPlayer.pause();
|
||||
mService.stopForegroundCompat(NOTIFICATION_ID);
|
||||
setState(STATE_NORMAL);
|
||||
}
|
||||
|
||||
private void setPlaying(boolean play)
|
||||
{
|
||||
if (play)
|
||||
play();
|
||||
else
|
||||
pause();
|
||||
}
|
||||
|
||||
private void setCurrentSong(int delta)
|
||||
{
|
||||
Song song = getSong(delta);
|
||||
if (song == null)
|
||||
return;
|
||||
|
||||
mCurrentSong += delta;
|
||||
|
||||
Song newSong = getSong(delta);
|
||||
int i = mWatchers.beginBroadcast();
|
||||
while (--i != -1) {
|
||||
try {
|
||||
if (delta < 0)
|
||||
mWatchers.getBroadcastItem(i).previousSong(song, newSong);
|
||||
else
|
||||
mWatchers.getBroadcastItem(i).nextSong(song, newSong);
|
||||
} catch (RemoteException e) {
|
||||
// Null elements will be removed automatically
|
||||
}
|
||||
}
|
||||
mWatchers.finishBroadcast();
|
||||
|
||||
try {
|
||||
mMediaPlayer.reset();
|
||||
mMediaPlayer.setDataSource(song.path);
|
||||
mMediaPlayer.prepare();
|
||||
if (mState == STATE_PLAYING)
|
||||
play();
|
||||
} catch (IOException e) {
|
||||
Log.e("Tumult", "IOException", e);
|
||||
}
|
||||
|
||||
mediaLengthChanged();
|
||||
|
||||
getSong(+2);
|
||||
|
||||
while (mCurrentSong > 15) {
|
||||
mSongTimeline.remove(0);
|
||||
--mCurrentSong;
|
||||
}
|
||||
}
|
||||
|
||||
private long getStartTime()
|
||||
{
|
||||
int position = mMediaPlayer.getCurrentPosition();
|
||||
return System.currentTimeMillis() - position;
|
||||
}
|
||||
|
||||
private void mediaLengthChanged()
|
||||
{
|
||||
long start = getStartTime();
|
||||
int duration = mMediaPlayer.getDuration();
|
||||
|
||||
int i = mWatchers.beginBroadcast();
|
||||
while (--i != -1) {
|
||||
try {
|
||||
mWatchers.getBroadcastItem(i).mediaLengthChanged(start, duration);
|
||||
} catch (RemoteException e) {
|
||||
// Null elements will be removed automatically
|
||||
}
|
||||
}
|
||||
mWatchers.finishBroadcast();
|
||||
}
|
||||
|
||||
public void onCompletion(MediaPlayer player)
|
||||
{
|
||||
mWakeLock.acquire();
|
||||
mHandler.sendEmptyMessage(TRACK_CHANGED);
|
||||
mHandler.sendEmptyMessage(RELEASE_WAKE_LOCK);
|
||||
}
|
||||
|
||||
private Song randomSong()
|
||||
{
|
||||
return new Song(mSongs[mRandom.nextInt(mSongs.length)]);
|
||||
}
|
||||
|
||||
private synchronized Song getSong(int delta)
|
||||
{
|
||||
int pos = mCurrentSong + delta;
|
||||
|
||||
if (pos < 0)
|
||||
return null;
|
||||
|
||||
int size = mSongTimeline.size();
|
||||
if (pos > size)
|
||||
return null;
|
||||
|
||||
if (pos == size) {
|
||||
if (mSongs == null)
|
||||
return null;
|
||||
mSongTimeline.add(randomSong());
|
||||
}
|
||||
|
||||
return mSongTimeline.get(pos);
|
||||
}
|
||||
|
||||
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context content, Intent intent)
|
||||
{
|
||||
if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG) && mHandler != null) {
|
||||
int plugged = intent.getIntExtra("state", 0) == 130 ? 1 : 0;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(HEADSET_PLUGGED, plugged, 0));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public void onSharedPreferenceChanged(SharedPreferences settings, String key)
|
||||
{
|
||||
if ("headset_only".equals(key) && mHandler != null) {
|
||||
int arg = settings.getBoolean(key, false) ? 1 : 0;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(HEADSET_PREF_CHANGED, arg, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
package org.kreed.tumult;
|
||||
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.util.Log;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Random;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.PowerManager;
|
||||
import android.os.RemoteCallbackList;
|
||||
import android.os.RemoteException;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
public class MusicPlayer implements Runnable, MediaPlayer.OnCompletionListener, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private static final int NOTIFICATION_ID = 2;
|
||||
|
||||
public static final int STATE_NORMAL = 0;
|
||||
public static final int STATE_NO_MEDIA = 1;
|
||||
public static final int STATE_PLAYING = 2;
|
||||
|
||||
public IPlaybackService.Stub mBinder = new IPlaybackService.Stub() {
|
||||
public Song[] getCurrentSongs()
|
||||
{
|
||||
return new Song[] { getSong(-1), getSong(0), getSong(1) };
|
||||
}
|
||||
|
||||
public int getState()
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
public long getStartTime()
|
||||
{
|
||||
if (mMediaPlayer == null)
|
||||
return 0;
|
||||
return MusicPlayer.this.getStartTime();
|
||||
}
|
||||
|
||||
public int getDuration()
|
||||
{
|
||||
if (mMediaPlayer == null)
|
||||
return 0;
|
||||
return mMediaPlayer.getDuration();
|
||||
}
|
||||
|
||||
public void nextSong()
|
||||
{
|
||||
if (mHandler == null)
|
||||
return;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(SET_SONG, 1, 0));
|
||||
}
|
||||
|
||||
public void previousSong()
|
||||
{
|
||||
if (mHandler == null)
|
||||
return;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(SET_SONG, -1, 0));
|
||||
}
|
||||
|
||||
public void togglePlayback()
|
||||
{
|
||||
if (mHandler == null)
|
||||
return;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(PLAY_PAUSE, 0, 0));
|
||||
}
|
||||
|
||||
public void registerWatcher(IMusicPlayerWatcher watcher)
|
||||
{
|
||||
if (watcher != null)
|
||||
mWatchers.register(watcher);
|
||||
}
|
||||
|
||||
public void seekToProgress(int progress)
|
||||
{
|
||||
if (mMediaPlayer == null || !mMediaPlayer.isPlaying())
|
||||
return;
|
||||
|
||||
long position = (long)mMediaPlayer.getDuration() * progress / 1000;
|
||||
mMediaPlayer.seekTo((int)position);
|
||||
mediaLengthChanged();
|
||||
}
|
||||
};
|
||||
|
||||
public void queueSong(int songId)
|
||||
{
|
||||
if (mHandler == null)
|
||||
return;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(QUEUE_ITEM, ITEM_SONG, songId));
|
||||
}
|
||||
|
||||
public void stopQueueing()
|
||||
{
|
||||
if (mHandler == null)
|
||||
return;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(QUEUE_ITEM, ITEM_RESET, 0));
|
||||
}
|
||||
|
||||
private PlaybackService mService;
|
||||
private RemoteCallbackList<IMusicPlayerWatcher> mWatchers;
|
||||
|
||||
private boolean mHeadsetOnly = true;
|
||||
|
||||
private Handler mHandler;
|
||||
private MediaPlayer mMediaPlayer;
|
||||
private Random mRandom;
|
||||
private PowerManager.WakeLock mWakeLock;
|
||||
|
||||
private int[] mSongs;
|
||||
private ArrayList<Song> mSongTimeline;
|
||||
private int mCurrentSong = -1;
|
||||
private int mQueuePos = 0;
|
||||
private boolean mPlugged = true;
|
||||
private int mState = STATE_NORMAL;
|
||||
|
||||
public MusicPlayer(PlaybackService service)
|
||||
{
|
||||
mService = service;
|
||||
mWatchers = new RemoteCallbackList<IMusicPlayerWatcher>();
|
||||
mSongTimeline = new ArrayList<Song>();
|
||||
|
||||
new Thread(this).start();
|
||||
}
|
||||
|
||||
private static final int SET_SONG = 0;
|
||||
private static final int PLAY_PAUSE = 1;
|
||||
private static final int HEADSET_PLUGGED = 2;
|
||||
private static final int HEADSET_PREF_CHANGED = 3;
|
||||
private static final int QUEUE_ITEM = 4;
|
||||
private static final int TRACK_CHANGED = 5;
|
||||
private static final int RELEASE_WAKE_LOCK = 6;
|
||||
|
||||
private static final int ITEM_SONG = 0;
|
||||
private static final int ITEM_RESET = 1;
|
||||
|
||||
public void run()
|
||||
{
|
||||
Looper.prepare();
|
||||
|
||||
mMediaPlayer = new MediaPlayer();
|
||||
mRandom = new Random();
|
||||
|
||||
mHandler = new Handler() {
|
||||
public void handleMessage(Message message)
|
||||
{
|
||||
switch (message.what) {
|
||||
case SET_SONG:
|
||||
setCurrentSong(message.arg1);
|
||||
break;
|
||||
case PLAY_PAUSE:
|
||||
if (mCurrentSong == -1) {
|
||||
setCurrentSong(+1);
|
||||
return;
|
||||
}
|
||||
|
||||
setPlaying(!mMediaPlayer.isPlaying());
|
||||
break;
|
||||
case HEADSET_PLUGGED:
|
||||
boolean plugged = message.arg1 == 1;
|
||||
if (plugged != mPlugged) {
|
||||
mPlugged = plugged;
|
||||
if (mCurrentSong == -1 || mPlugged == mMediaPlayer.isPlaying())
|
||||
return;
|
||||
setPlaying(mPlugged);
|
||||
}
|
||||
break;
|
||||
case HEADSET_PREF_CHANGED:
|
||||
mHeadsetOnly = message.arg1 == 1;
|
||||
if (mHeadsetOnly && !mPlugged && mMediaPlayer.isPlaying())
|
||||
pause();
|
||||
break;
|
||||
case QUEUE_ITEM:
|
||||
switch (message.arg1) {
|
||||
case ITEM_SONG:
|
||||
int i = mCurrentSong + 1 + mQueuePos++;
|
||||
Song song = new Song(message.arg2);
|
||||
Toast.makeText(Tumult.getContext(), "Enqueued " + song.title, Toast.LENGTH_SHORT).show();
|
||||
|
||||
if (i < mSongTimeline.size())
|
||||
mSongTimeline.set(i, song);
|
||||
else
|
||||
mSongTimeline.add(song);
|
||||
break;
|
||||
case ITEM_RESET:
|
||||
mQueuePos = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case TRACK_CHANGED:
|
||||
setCurrentSong(+1);
|
||||
break;
|
||||
case RELEASE_WAKE_LOCK:
|
||||
if (mWakeLock.isHeld())
|
||||
mWakeLock.release();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mService.registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
|
||||
|
||||
PowerManager powerManager = (PowerManager)mService.getSystemService(Context.POWER_SERVICE);
|
||||
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "TumultSongChangeLock");
|
||||
|
||||
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
||||
mMediaPlayer.setWakeMode(mService, PowerManager.PARTIAL_WAKE_LOCK);
|
||||
mMediaPlayer.setOnCompletionListener(this);
|
||||
retrieveSongs();
|
||||
|
||||
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(mService);
|
||||
mHeadsetOnly = settings.getBoolean("headset_only", false);
|
||||
settings.registerOnSharedPreferenceChangeListener(this);
|
||||
|
||||
mHandler.post(new Runnable() {
|
||||
public void run()
|
||||
{
|
||||
setCurrentSong(1);
|
||||
}
|
||||
});
|
||||
|
||||
Looper.loop();
|
||||
}
|
||||
|
||||
public void release()
|
||||
{
|
||||
if (mMediaPlayer != null) {
|
||||
pause();
|
||||
mMediaPlayer.release();
|
||||
mMediaPlayer = null;
|
||||
}
|
||||
|
||||
if (mWakeLock != null && mWakeLock.isHeld())
|
||||
mWakeLock.release();
|
||||
}
|
||||
|
||||
public void setState(int state)
|
||||
{
|
||||
if (mState == state)
|
||||
return;
|
||||
|
||||
int oldState = mState;
|
||||
mState = state;
|
||||
|
||||
int i = mWatchers.beginBroadcast();
|
||||
while (--i != -1) {
|
||||
try {
|
||||
mWatchers.getBroadcastItem(i).stateChanged(oldState, mState);
|
||||
} catch (RemoteException e) {
|
||||
// Null elements will be removed automatically
|
||||
}
|
||||
}
|
||||
mWatchers.finishBroadcast();
|
||||
}
|
||||
|
||||
private void retrieveSongs()
|
||||
{
|
||||
mSongs = Song.getAllSongs();
|
||||
if (mSongs == null && mState == STATE_NORMAL)
|
||||
setState(STATE_NO_MEDIA);
|
||||
}
|
||||
|
||||
private void play()
|
||||
{
|
||||
if (mHeadsetOnly && !mPlugged)
|
||||
return;
|
||||
|
||||
mMediaPlayer.start();
|
||||
|
||||
Song song = getSong(0);
|
||||
|
||||
RemoteViews views = new RemoteViews(mService.getPackageName(), R.layout.statusbar);
|
||||
views.setImageViewResource(R.id.icon, R.drawable.status_icon);
|
||||
views.setTextViewText(R.id.title, song.title);
|
||||
views.setTextViewText(R.id.artist, song.artist);
|
||||
|
||||
Notification notification = new Notification();
|
||||
notification.contentView = views;
|
||||
notification.icon = R.drawable.status_icon;
|
||||
notification.flags |= Notification.FLAG_ONGOING_EVENT;
|
||||
Intent intent = new Intent(mService, NowPlayingActivity.class);
|
||||
notification.contentIntent = PendingIntent.getActivity(mService, 0, intent, 0);
|
||||
|
||||
mService.startForegroundCompat(NOTIFICATION_ID, notification);
|
||||
|
||||
setState(STATE_PLAYING);
|
||||
}
|
||||
|
||||
private void pause()
|
||||
{
|
||||
mMediaPlayer.pause();
|
||||
mService.stopForegroundCompat(NOTIFICATION_ID);
|
||||
setState(STATE_NORMAL);
|
||||
}
|
||||
|
||||
private void setPlaying(boolean play)
|
||||
{
|
||||
if (play)
|
||||
play();
|
||||
else
|
||||
pause();
|
||||
}
|
||||
|
||||
private void setCurrentSong(int delta)
|
||||
{
|
||||
Song song = getSong(delta);
|
||||
if (song == null)
|
||||
return;
|
||||
|
||||
mCurrentSong += delta;
|
||||
|
||||
Song newSong = getSong(delta);
|
||||
int i = mWatchers.beginBroadcast();
|
||||
while (--i != -1) {
|
||||
try {
|
||||
if (delta < 0)
|
||||
mWatchers.getBroadcastItem(i).previousSong(song, newSong);
|
||||
else
|
||||
mWatchers.getBroadcastItem(i).nextSong(song, newSong);
|
||||
} catch (RemoteException e) {
|
||||
// Null elements will be removed automatically
|
||||
}
|
||||
}
|
||||
mWatchers.finishBroadcast();
|
||||
|
||||
try {
|
||||
mMediaPlayer.reset();
|
||||
mMediaPlayer.setDataSource(song.path);
|
||||
mMediaPlayer.prepare();
|
||||
if (mState == STATE_PLAYING)
|
||||
play();
|
||||
} catch (IOException e) {
|
||||
Log.e("Tumult", "IOException", e);
|
||||
}
|
||||
|
||||
mediaLengthChanged();
|
||||
|
||||
getSong(+2);
|
||||
|
||||
while (mCurrentSong > 15) {
|
||||
mSongTimeline.remove(0);
|
||||
--mCurrentSong;
|
||||
}
|
||||
}
|
||||
|
||||
private long getStartTime()
|
||||
{
|
||||
int position = mMediaPlayer.getCurrentPosition();
|
||||
return System.currentTimeMillis() - position;
|
||||
}
|
||||
|
||||
private void mediaLengthChanged()
|
||||
{
|
||||
long start = getStartTime();
|
||||
int duration = mMediaPlayer.getDuration();
|
||||
|
||||
int i = mWatchers.beginBroadcast();
|
||||
while (--i != -1) {
|
||||
try {
|
||||
mWatchers.getBroadcastItem(i).mediaLengthChanged(start, duration);
|
||||
} catch (RemoteException e) {
|
||||
// Null elements will be removed automatically
|
||||
}
|
||||
}
|
||||
mWatchers.finishBroadcast();
|
||||
}
|
||||
|
||||
public void onCompletion(MediaPlayer player)
|
||||
{
|
||||
mWakeLock.acquire();
|
||||
mHandler.sendEmptyMessage(TRACK_CHANGED);
|
||||
mHandler.sendEmptyMessage(RELEASE_WAKE_LOCK);
|
||||
}
|
||||
|
||||
private Song randomSong()
|
||||
{
|
||||
return new Song(mSongs[mRandom.nextInt(mSongs.length)]);
|
||||
}
|
||||
|
||||
private synchronized Song getSong(int delta)
|
||||
{
|
||||
int pos = mCurrentSong + delta;
|
||||
|
||||
if (pos < 0)
|
||||
return null;
|
||||
|
||||
int size = mSongTimeline.size();
|
||||
if (pos > size)
|
||||
return null;
|
||||
|
||||
if (pos == size) {
|
||||
if (mSongs == null)
|
||||
return null;
|
||||
mSongTimeline.add(randomSong());
|
||||
}
|
||||
|
||||
return mSongTimeline.get(pos);
|
||||
}
|
||||
|
||||
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context content, Intent intent)
|
||||
{
|
||||
if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG) && mHandler != null) {
|
||||
int plugged = intent.getIntExtra("state", 0) == 130 ? 1 : 0;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(HEADSET_PLUGGED, plugged, 0));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public void onSharedPreferenceChanged(SharedPreferences settings, String key)
|
||||
{
|
||||
if ("headset_only".equals(key) && mHandler != null) {
|
||||
int arg = settings.getBoolean(key, false) ? 1 : 0;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(HEADSET_PREF_CHANGED, arg, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,96 +1,96 @@
|
||||
package org.kreed.tumult;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
public class PlaybackService extends Service {
|
||||
private MusicPlayer mPlayer;
|
||||
private NotificationManager mNotificationManager;
|
||||
private Method mStartForeground;
|
||||
private Method mStopForeground;
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent)
|
||||
{
|
||||
if (mPlayer == null)
|
||||
return null;
|
||||
return mPlayer.mBinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate()
|
||||
{
|
||||
mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
|
||||
try {
|
||||
mStartForeground = getClass().getMethod("startForeground", new Class[] { int.class, Notification.class });
|
||||
mStopForeground = getClass().getMethod("stopForeground", new Class[] { boolean.class });
|
||||
} catch (NoSuchMethodException e) {
|
||||
// Running on an older platform.
|
||||
mStartForeground = mStopForeground = null;
|
||||
}
|
||||
|
||||
mPlayer = new MusicPlayer(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(Intent intent, int flags)
|
||||
{
|
||||
int id;
|
||||
|
||||
if ((id = intent.getIntExtra("songId", -1)) != -1)
|
||||
mPlayer.queueSong(id);
|
||||
else
|
||||
mPlayer.stopQueueing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy()
|
||||
{
|
||||
super.onDestroy();
|
||||
if (mPlayer != null) {
|
||||
mPlayer.release();
|
||||
mPlayer = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void startForegroundCompat(int id, Notification notification)
|
||||
{
|
||||
if (mStartForeground == null) {
|
||||
setForeground(true);
|
||||
mNotificationManager.notify(id, notification);
|
||||
} else {
|
||||
Object[] startForegroundArgs = { Integer.valueOf(id), notification };
|
||||
try {
|
||||
mStartForeground.invoke(this, startForegroundArgs);
|
||||
} catch (InvocationTargetException e) {
|
||||
Log.w("Tumult", "Unable to invoke startForeground", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.w("Tumult", "Unable to invoke startForeground", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stopForegroundCompat(int id)
|
||||
{
|
||||
if (mStopForeground == null) {
|
||||
mNotificationManager.cancel(id);
|
||||
setForeground(false);
|
||||
} else {
|
||||
Object[] topForegroundArgs = { Boolean.TRUE };
|
||||
try {
|
||||
mStopForeground.invoke(this, topForegroundArgs);
|
||||
} catch (InvocationTargetException e) {
|
||||
Log.w("Tumult", "Unable to invoke stopForeground", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.w("Tumult", "Unable to invoke stopForeground", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
package org.kreed.tumult;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
public class PlaybackService extends Service {
|
||||
private MusicPlayer mPlayer;
|
||||
private NotificationManager mNotificationManager;
|
||||
private Method mStartForeground;
|
||||
private Method mStopForeground;
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent)
|
||||
{
|
||||
if (mPlayer == null)
|
||||
return null;
|
||||
return mPlayer.mBinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate()
|
||||
{
|
||||
mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
|
||||
try {
|
||||
mStartForeground = getClass().getMethod("startForeground", new Class[] { int.class, Notification.class });
|
||||
mStopForeground = getClass().getMethod("stopForeground", new Class[] { boolean.class });
|
||||
} catch (NoSuchMethodException e) {
|
||||
// Running on an older platform.
|
||||
mStartForeground = mStopForeground = null;
|
||||
}
|
||||
|
||||
mPlayer = new MusicPlayer(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(Intent intent, int flags)
|
||||
{
|
||||
int id;
|
||||
|
||||
if ((id = intent.getIntExtra("songId", -1)) != -1)
|
||||
mPlayer.queueSong(id);
|
||||
else
|
||||
mPlayer.stopQueueing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy()
|
||||
{
|
||||
super.onDestroy();
|
||||
if (mPlayer != null) {
|
||||
mPlayer.release();
|
||||
mPlayer = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void startForegroundCompat(int id, Notification notification)
|
||||
{
|
||||
if (mStartForeground == null) {
|
||||
setForeground(true);
|
||||
mNotificationManager.notify(id, notification);
|
||||
} else {
|
||||
Object[] startForegroundArgs = { Integer.valueOf(id), notification };
|
||||
try {
|
||||
mStartForeground.invoke(this, startForegroundArgs);
|
||||
} catch (InvocationTargetException e) {
|
||||
Log.w("Tumult", "Unable to invoke startForeground", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.w("Tumult", "Unable to invoke startForeground", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stopForegroundCompat(int id)
|
||||
{
|
||||
if (mStopForeground == null) {
|
||||
mNotificationManager.cancel(id);
|
||||
setForeground(false);
|
||||
} else {
|
||||
Object[] topForegroundArgs = { Boolean.TRUE };
|
||||
try {
|
||||
mStopForeground.invoke(this, topForegroundArgs);
|
||||
} catch (InvocationTargetException e) {
|
||||
Log.w("Tumult", "Unable to invoke stopForeground", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.w("Tumult", "Unable to invoke stopForeground", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
package org.kreed.tumult;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceActivity;
|
||||
|
||||
public class PreferencesActivity extends PreferenceActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
}
|
||||
package org.kreed.tumult;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceActivity;
|
||||
|
||||
public class PreferencesActivity extends PreferenceActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
}
|
||||
}
|
@ -1,3 +1,3 @@
|
||||
package org.kreed.tumult;
|
||||
|
||||
package org.kreed.tumult;
|
||||
|
||||
parcelable Song;
|
@ -1,113 +1,113 @@
|
||||
package org.kreed.tumult;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
public class Song implements Parcelable {
|
||||
public String path;
|
||||
public String coverPath;
|
||||
|
||||
public String title;
|
||||
public String album;
|
||||
public String artist;
|
||||
|
||||
public Song(int id)
|
||||
{
|
||||
Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||
String[] projection = {
|
||||
MediaStore.Audio.Media.DATA,
|
||||
MediaStore.Audio.Media.TITLE,
|
||||
MediaStore.Audio.Media.ALBUM,
|
||||
MediaStore.Audio.Media.ARTIST,
|
||||
MediaStore.Audio.Media.ALBUM_ID
|
||||
};
|
||||
String selection = MediaStore.Audio.Media._ID + "==" + id;;
|
||||
|
||||
ContentResolver resolver = Tumult.getContext().getContentResolver();
|
||||
Cursor cursor = resolver.query(media, projection, selection, null, null);
|
||||
|
||||
if (cursor != null && cursor.moveToNext()) {
|
||||
path = cursor.getString(0);
|
||||
title = cursor.getString(1);
|
||||
album = cursor.getString(2);
|
||||
artist = cursor.getString(3);
|
||||
String albumId = cursor.getString(4);
|
||||
cursor.close();
|
||||
|
||||
media = MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI;
|
||||
String[] albumProjection = { MediaStore.Audio.Albums.ALBUM_ART };
|
||||
String albumSelection = MediaStore.Audio.Albums._ID + "==" + albumId;
|
||||
|
||||
cursor = resolver.query(media, albumProjection, albumSelection, null, null);
|
||||
if (cursor != null && cursor.moveToNext()) {
|
||||
coverPath = cursor.getString(0);
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int[] getAllSongs()
|
||||
{
|
||||
Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||
String[] projection = { MediaStore.Audio.Media._ID };
|
||||
String selection = MediaStore.Audio.Media.IS_MUSIC + "!=0";
|
||||
|
||||
ContentResolver resolver = Tumult.getContext().getContentResolver();
|
||||
Cursor cursor = resolver.query(media, projection, selection, null, null);
|
||||
|
||||
if (cursor == null)
|
||||
return null;
|
||||
|
||||
int count = cursor.getCount();
|
||||
if (count == 0)
|
||||
return null;
|
||||
|
||||
int[] songs = new int[count];
|
||||
while (--count != -1 && cursor.moveToNext())
|
||||
songs[count] = cursor.getInt(0);
|
||||
|
||||
cursor.close();
|
||||
cursor = null;
|
||||
|
||||
return songs;
|
||||
}
|
||||
|
||||
public static Parcelable.Creator<Song> CREATOR = new Parcelable.Creator<Song>() {
|
||||
public Song createFromParcel(Parcel in)
|
||||
{
|
||||
return new Song(in);
|
||||
}
|
||||
|
||||
public Song[] newArray(int size)
|
||||
{
|
||||
return new Song[size];
|
||||
}
|
||||
};
|
||||
|
||||
public Song(Parcel in)
|
||||
{
|
||||
path = in.readString();
|
||||
coverPath = in.readString();
|
||||
title = in.readString();
|
||||
album = in.readString();
|
||||
artist = in.readString();
|
||||
}
|
||||
|
||||
public void writeToParcel(Parcel out, int flags)
|
||||
{
|
||||
out.writeString(path);
|
||||
out.writeString(coverPath);
|
||||
out.writeString(title);
|
||||
out.writeString(album);
|
||||
out.writeString(artist);
|
||||
}
|
||||
|
||||
public int describeContents()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
package org.kreed.tumult;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
public class Song implements Parcelable {
|
||||
public String path;
|
||||
public String coverPath;
|
||||
|
||||
public String title;
|
||||
public String album;
|
||||
public String artist;
|
||||
|
||||
public Song(int id)
|
||||
{
|
||||
Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||
String[] projection = {
|
||||
MediaStore.Audio.Media.DATA,
|
||||
MediaStore.Audio.Media.TITLE,
|
||||
MediaStore.Audio.Media.ALBUM,
|
||||
MediaStore.Audio.Media.ARTIST,
|
||||
MediaStore.Audio.Media.ALBUM_ID
|
||||
};
|
||||
String selection = MediaStore.Audio.Media._ID + "==" + id;;
|
||||
|
||||
ContentResolver resolver = Tumult.getContext().getContentResolver();
|
||||
Cursor cursor = resolver.query(media, projection, selection, null, null);
|
||||
|
||||
if (cursor != null && cursor.moveToNext()) {
|
||||
path = cursor.getString(0);
|
||||
title = cursor.getString(1);
|
||||
album = cursor.getString(2);
|
||||
artist = cursor.getString(3);
|
||||
String albumId = cursor.getString(4);
|
||||
cursor.close();
|
||||
|
||||
media = MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI;
|
||||
String[] albumProjection = { MediaStore.Audio.Albums.ALBUM_ART };
|
||||
String albumSelection = MediaStore.Audio.Albums._ID + "==" + albumId;
|
||||
|
||||
cursor = resolver.query(media, albumProjection, albumSelection, null, null);
|
||||
if (cursor != null && cursor.moveToNext()) {
|
||||
coverPath = cursor.getString(0);
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int[] getAllSongs()
|
||||
{
|
||||
Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||
String[] projection = { MediaStore.Audio.Media._ID };
|
||||
String selection = MediaStore.Audio.Media.IS_MUSIC + "!=0";
|
||||
|
||||
ContentResolver resolver = Tumult.getContext().getContentResolver();
|
||||
Cursor cursor = resolver.query(media, projection, selection, null, null);
|
||||
|
||||
if (cursor == null)
|
||||
return null;
|
||||
|
||||
int count = cursor.getCount();
|
||||
if (count == 0)
|
||||
return null;
|
||||
|
||||
int[] songs = new int[count];
|
||||
while (--count != -1 && cursor.moveToNext())
|
||||
songs[count] = cursor.getInt(0);
|
||||
|
||||
cursor.close();
|
||||
cursor = null;
|
||||
|
||||
return songs;
|
||||
}
|
||||
|
||||
public static Parcelable.Creator<Song> CREATOR = new Parcelable.Creator<Song>() {
|
||||
public Song createFromParcel(Parcel in)
|
||||
{
|
||||
return new Song(in);
|
||||
}
|
||||
|
||||
public Song[] newArray(int size)
|
||||
{
|
||||
return new Song[size];
|
||||
}
|
||||
};
|
||||
|
||||
public Song(Parcel in)
|
||||
{
|
||||
path = in.readString();
|
||||
coverPath = in.readString();
|
||||
title = in.readString();
|
||||
album = in.readString();
|
||||
artist = in.readString();
|
||||
}
|
||||
|
||||
public void writeToParcel(Parcel out, int flags)
|
||||
{
|
||||
out.writeString(path);
|
||||
out.writeString(coverPath);
|
||||
out.writeString(title);
|
||||
out.writeString(album);
|
||||
out.writeString(artist);
|
||||
}
|
||||
|
||||
public int describeContents()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -1,109 +1,109 @@
|
||||
package org.kreed.tumult;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
|
||||
public class SongSelector extends Activity implements View.OnClickListener, OnItemClickListener {
|
||||
private ListView mListView;
|
||||
private SongAdapter mAdapter;
|
||||
|
||||
private LinearLayout mFilterLayout;
|
||||
private TextView mFilterText;
|
||||
private Button mBackspaceButton;
|
||||
private Button mCloseButton;
|
||||
private View mNumpad;
|
||||
private Button[] mButtons;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle)
|
||||
{
|
||||
super.onCreate(icicle);
|
||||
|
||||
setContentView(R.layout.songselector);
|
||||
mListView = (ListView)findViewById(R.id.song_list);
|
||||
mAdapter = new SongAdapter(this);
|
||||
mListView.setAdapter(mAdapter);
|
||||
mListView.setTextFilterEnabled(true);
|
||||
mListView.setOnItemClickListener(this);
|
||||
|
||||
mFilterLayout = (LinearLayout)findViewById(R.id.filter_layout);
|
||||
mFilterText = (TextView)findViewById(R.id.filter_text);
|
||||
mBackspaceButton = (Button)findViewById(R.id.backspace);
|
||||
mBackspaceButton.setOnClickListener(this);
|
||||
mCloseButton = (Button)findViewById(R.id.close);
|
||||
mCloseButton.setOnClickListener(this);
|
||||
|
||||
mNumpad = findViewById(R.id.numpad);
|
||||
|
||||
Configuration config = getResources().getConfiguration();
|
||||
boolean hasKeyboard = config.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO && config.keyboard == Configuration.KEYBOARD_QWERTY;
|
||||
mNumpad.setVisibility(hasKeyboard ? View.GONE : View.VISIBLE);
|
||||
|
||||
mButtons = new Button[] {
|
||||
(Button)findViewById(R.id.Button1),
|
||||
(Button)findViewById(R.id.Button2),
|
||||
(Button)findViewById(R.id.Button3),
|
||||
(Button)findViewById(R.id.Button4),
|
||||
(Button)findViewById(R.id.Button5),
|
||||
(Button)findViewById(R.id.Button6),
|
||||
(Button)findViewById(R.id.Button7),
|
||||
(Button)findViewById(R.id.Button8),
|
||||
(Button)findViewById(R.id.Button9)
|
||||
};
|
||||
|
||||
for (Button button : mButtons)
|
||||
button.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSearchRequested()
|
||||
{
|
||||
mNumpad.setVisibility(mNumpad.getVisibility() == View.GONE ? View.VISIBLE : View.GONE);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void onClick(View view)
|
||||
{
|
||||
int visible = View.VISIBLE;
|
||||
String text = mFilterText.getText().toString();
|
||||
if (text.length() == 0)
|
||||
text = "Filter: ";
|
||||
if (view == mCloseButton) {
|
||||
visible = View.GONE;
|
||||
text = null;
|
||||
} else if (view == mBackspaceButton) {
|
||||
if (text.length() > 8)
|
||||
text = text.substring(0, text.length() - 1);
|
||||
} else {
|
||||
int i = -1;
|
||||
while (++i != mButtons.length)
|
||||
if (mButtons[i] == view)
|
||||
break;
|
||||
|
||||
text += i + 1;
|
||||
}
|
||||
|
||||
mFilterText.setText(text);
|
||||
mFilterLayout.setVisibility(visible);
|
||||
mListView.setTextFilterEnabled(visible == View.VISIBLE);
|
||||
|
||||
String filterText = text == null || text.length() <= 8 ? null : text.substring(8);
|
||||
mAdapter.getFilter().filter(filterText);
|
||||
}
|
||||
|
||||
public void onItemClick(AdapterView<?> list, View view, int pos, long id)
|
||||
{
|
||||
Intent intent = new Intent(this, PlaybackService.class);
|
||||
intent.putExtra("songId", (int)id);
|
||||
startService(intent);
|
||||
}
|
||||
package org.kreed.tumult;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
|
||||
public class SongSelector extends Activity implements View.OnClickListener, OnItemClickListener {
|
||||
private ListView mListView;
|
||||
private SongAdapter mAdapter;
|
||||
|
||||
private LinearLayout mFilterLayout;
|
||||
private TextView mFilterText;
|
||||
private Button mBackspaceButton;
|
||||
private Button mCloseButton;
|
||||
private View mNumpad;
|
||||
private Button[] mButtons;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle)
|
||||
{
|
||||
super.onCreate(icicle);
|
||||
|
||||
setContentView(R.layout.songselector);
|
||||
mListView = (ListView)findViewById(R.id.song_list);
|
||||
mAdapter = new SongAdapter(this);
|
||||
mListView.setAdapter(mAdapter);
|
||||
mListView.setTextFilterEnabled(true);
|
||||
mListView.setOnItemClickListener(this);
|
||||
|
||||
mFilterLayout = (LinearLayout)findViewById(R.id.filter_layout);
|
||||
mFilterText = (TextView)findViewById(R.id.filter_text);
|
||||
mBackspaceButton = (Button)findViewById(R.id.backspace);
|
||||
mBackspaceButton.setOnClickListener(this);
|
||||
mCloseButton = (Button)findViewById(R.id.close);
|
||||
mCloseButton.setOnClickListener(this);
|
||||
|
||||
mNumpad = findViewById(R.id.numpad);
|
||||
|
||||
Configuration config = getResources().getConfiguration();
|
||||
boolean hasKeyboard = config.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO && config.keyboard == Configuration.KEYBOARD_QWERTY;
|
||||
mNumpad.setVisibility(hasKeyboard ? View.GONE : View.VISIBLE);
|
||||
|
||||
mButtons = new Button[] {
|
||||
(Button)findViewById(R.id.Button1),
|
||||
(Button)findViewById(R.id.Button2),
|
||||
(Button)findViewById(R.id.Button3),
|
||||
(Button)findViewById(R.id.Button4),
|
||||
(Button)findViewById(R.id.Button5),
|
||||
(Button)findViewById(R.id.Button6),
|
||||
(Button)findViewById(R.id.Button7),
|
||||
(Button)findViewById(R.id.Button8),
|
||||
(Button)findViewById(R.id.Button9)
|
||||
};
|
||||
|
||||
for (Button button : mButtons)
|
||||
button.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSearchRequested()
|
||||
{
|
||||
mNumpad.setVisibility(mNumpad.getVisibility() == View.GONE ? View.VISIBLE : View.GONE);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void onClick(View view)
|
||||
{
|
||||
int visible = View.VISIBLE;
|
||||
String text = mFilterText.getText().toString();
|
||||
if (text.length() == 0)
|
||||
text = "Filter: ";
|
||||
if (view == mCloseButton) {
|
||||
visible = View.GONE;
|
||||
text = null;
|
||||
} else if (view == mBackspaceButton) {
|
||||
if (text.length() > 8)
|
||||
text = text.substring(0, text.length() - 1);
|
||||
} else {
|
||||
int i = -1;
|
||||
while (++i != mButtons.length)
|
||||
if (mButtons[i] == view)
|
||||
break;
|
||||
|
||||
text += i + 1;
|
||||
}
|
||||
|
||||
mFilterText.setText(text);
|
||||
mFilterLayout.setVisibility(visible);
|
||||
mListView.setTextFilterEnabled(visible == View.VISIBLE);
|
||||
|
||||
String filterText = text == null || text.length() <= 8 ? null : text.substring(8);
|
||||
mAdapter.getFilter().filter(filterText);
|
||||
}
|
||||
|
||||
public void onItemClick(AdapterView<?> list, View view, int pos, long id)
|
||||
{
|
||||
Intent intent = new Intent(this, PlaybackService.class);
|
||||
intent.putExtra("songId", (int)id);
|
||||
startService(intent);
|
||||
}
|
||||
}
|
@ -1,18 +1,18 @@
|
||||
package org.kreed.tumult;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
|
||||
public class Tumult extends Application {
|
||||
private static Tumult instance;
|
||||
|
||||
public Tumult()
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
|
||||
public static Context getContext()
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
package org.kreed.tumult;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
|
||||
public class Tumult extends Application {
|
||||
private static Tumult instance;
|
||||
|
||||
public Tumult()
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
|
||||
public static Context getContext()
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user