私は次のBaseClassを持っています:
public class BaseClass extends Activity implements MusicUtils.Defs,
View.OnTouchListener, View.OnLongClickListener {
private static final int USE_AS_RINGTONE = CHILD_MENU_BASE;
private boolean mOneShot = false;
private boolean mSeeking = false;
private boolean mDeviceHasDpad;
private long mStartSeekenter code herePos = 0;
private long mLastSeekEventTime;
private IMediaPlaybackService mService = null;
private RepeatingImageButton mPrevButton;
private ImageButton mPauseButton;
private RepeatingImageButton mNextButton;
private Worker mAlbumArtWorker;
private AlbumArtHandler mAlbumArtHandler;
private Toast mToast;
private int mTouchSlop;
private ServiceToken mToken;
public BaseClass() {
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
mAlbumArtWorker = new Worker("album art worker");
mAlbumArtHandler = new AlbumArtHandler(mAlbumArtWorker.getLooper());
mCurrentTime = (TextView) findViewById(R.id.currenttime);
mTotalTime = (TextView) findViewById(R.id.totaltime);
mPrevButton = (RepeatingImageButton) findViewById(R.id.prev);
mPrevButton.setOnClickListener(mPrevListener);
mPrevButton.setRepeatListener(mRewListener, 260);
mPauseButton = (ImageButton) findViewById(R.id.pause);
mPauseButton.requestFocus();
mPauseButton.setOnClickListener(mPauseListener);
mNextButton = (RepeatingImageButton) findViewById(R.id.next);
mNextButton.setOnClickListener(mNextListener);
mNextButton.setRepeatListener(mFfwdListener, 260);
seekmethod = 1;
mDeviceHasDpad = (getResources().getConfiguration().navigation ==
Configuration.NAVIGATION_DPAD);
if (icicle != null) {
mOneShot = icicle.getBoolean("oneshot");
} else {
mOneShot = getIntent().getBooleanExtra("oneshot", false);
}
mTouchSlop = ViewConfiguration.get(BaseClass.this).getScaledTouchSlop();
}
Handler mLabelScroller = new Handler() {
@Override
public void handleMessage(Message msg) {
TextView tv = (TextView) msg.obj;
int x = tv.getScrollX();
x = x * 3 / 4;
tv.scrollTo(x, 0);
if (x == 0) {
tv.setEllipsize(TruncateAt.END);
} else {
Message newmsg = obtainMessage(0, tv);
mLabelScroller.sendMessageDelayed(newmsg, 15);
}
}
};
Cursor c = MusicUtils.query(this, ContentUris.withAppendedId(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, audioid),
new String[] { MediaStore.Audio.Media.IS_MUSIC }, null,
null,
null);
boolean ismusic = true;
if (c != null) {
if (c.moveToFirst()) {
ismusic = c.getInt(0) != 0;
}
c.close();
}
if (!ismusic) {
return false;
}
boolean knownartist = (artist != null)
&& !MediaStore.UNKNOWN_STRING.equals(artist);
boolean knownalbum = (album != null)
&& !MediaStore.UNKNOWN_STRING.equals(album);
title = getString(R.string.mediasearch, title);
Intent i = new Intent();
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.setAction(MediaStore.INTENT_ACTION_MEDIA_SEARCH);
i.putExtra(SearchManager.QUERY, query);
if (knownartist) {
i.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, artist);
}
if (knownalbum) {
i.putExtra(MediaStore.EXTRA_MEDIA_ALBUM, album);
}
i.putExtra(MediaStore.EXTRA_MEDIA_TITLE, song);
i.putExtra(MediaStore.EXTRA_MEDIA_FOCUS, mime);
startActivity(Intent.createChooser(i, title));
return true;
}
private OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
public void onStartTrackingTouch(SeekBar bar) {
mLastSeekEventTime = 0;
mFromTouch = true;
}
public void onProgressChanged(SeekBar bar, int progress,
boolean fromuser) {
if (!fromuser || (mService == null))
return;
long now = SystemClock.elapsedRealtime();
if ((now - mLastSeekEventTime) > 250) {
mLastSeekEventTime = now;
updateTrackInfo();
mPosOverride = mDuration * progress / 1000;
try {
mService.seek(mPosOverride);
} catch (RemoteException ex) {
}
// trackball event, allow progress updates
if (!mFromTouch) {
refreshNow();
mPosOverride = -1;
}
}
}
private View.OnClickListener mPauseListener = new View.OnClickListener() {
public void onClick(View v) {
doPauseResume();
}
};
private View.OnClickListener mPrevListener = new View.OnClickListener() {
public void onClick(View v) {
if (mService == null)
return;
try {
if (mService.position() < 2000) {
mService.prev();
} else {
mService.seek(0);
mService.play();
}
} catch (RemoteException ex) {
}
}
};
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
try {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
if (!useDpadMusicControl()) {
break;
}
if (mService != null) {
if (!mSeeking && mStartSeekPos >= 0) {
mPauseButton.requestFocus();
if (mStartSeekPos < 1000) {
mService.prev();
} else {
mService.seek(0);
}
} else {
scanBackward(-1,
event.getEventTime() -
event.getDownTime());
mPauseButton.requestFocus();
mStartSeekPos = -1;
}
}
mSeeking = false;
mPosOverride = -1;
return true;
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (!useDpadMusicControl()) {
break;
}
if (mService != null) {
if (!mSeeking && mStartSeekPos >= 0) {
mPauseButton.requestFocus();
mService.next();
} else {
scanForward(-1,
event.getEventTime() -
event.getDownTime());
mPauseButton.requestFocus();
mStartSeekPos = -1;
}
}
mSeeking = false;
mPosOverride = -1;
return true;
}
} catch (RemoteException ex) {
}
return super.onKeyUp(keyCode, event);
}
private boolean useDpadMusicControl() {
if (mDeviceHasDpad
&& (mPrevButton.isFocused() || mNextButton.isFocused()
|| mPauseButton
.isFocused())) {
return true;
}
return false;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
int repcnt = event.getRepeatCount();
if ((seekmethod == 0) ? seekMethod1(keyCode) : seekMethod2(keyCode))
return true;
switch (keyCode) {
/*
* // image scale case KeyEvent.KEYCODE_Q: av.adjustParams(-0.05, 0.0,
* 0.0, 0.0, 0.0,-1.0); break; case KeyEvent.KEYCODE_E: av.adjustParams(
* 0.05, 0.0, 0.0, 0.0, 0.0, 1.0); break; // image translate case
* KeyEvent.KEYCODE_W: av.adjustParams( 0.0, 0.0,-1.0, 0.0, 0.0, 0.0);
* break; case KeyEvent.KEYCODE_X: av.adjustParams( 0.0, 0.0, 1.0, 0.0,
* 0.0, 0.0); break; case KeyEvent.KEYCODE_A: av.adjustParams( 0.0,-1.0,
* 0.0, 0.0, 0.0, 0.0); break; case KeyEvent.KEYCODE_D: av.adjustParams(
* 0.0, 1.0, 0.0, 0.0, 0.0, 0.0); break; // camera rotation case
* KeyEvent.KEYCODE_R: av.adjustParams( 0.0, 0.0, 0.0, 0.0, 0.0,-1.0);
* break; case KeyEvent.KEYCODE_U: av.adjustParams( 0.0, 0.0, 0.0, 0.0,
* 0.0, 1.0); break; // camera translate case KeyEvent.KEYCODE_Y:
* av.adjustParams( 0.0, 0.0, 0.0, 0.0,-1.0, 0.0); break; case
* KeyEvent.KEYCODE_N: av.adjustParams( 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
* break; case KeyEvent.KEYCODE_G: av.adjustParams( 0.0, 0.0, 0.0,-1.0,
* 0.0, 0.0); break; case KeyEvent.KEYCODE_J: av.adjustParams( 0.0, 0.0,
* 0.0, 1.0, 0.0, 0.0); break;
*/
case KeyEvent.KEYCODE_SLASH:
seekmethod = 1 - seekmethod;
return true;
case KeyEvent.KEYCODE_DPAD_LEFT:
if (!useDpadMusicControl()) {
break;
}
if (!mPrevButton.hasFocus()) {
mPrevButton.requestFocus();
}
scanBackward(repcnt, event.getEventTime() -
event.getDownTime());
return true;
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (!useDpadMusicControl()) {
break;
}
if (!mNextButton.hasFocus()) {
mNextButton.requestFocus();
}
scanForward(repcnt, event.getEventTime() - event.getDownTime());
return true;
case KeyEvent.KEYCODE_S:
toggleShuffle();
return true;
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_SPACE:
doPauseResume();
return true;
}
return super.onKeyDown(keyCode, event);
}
private void scanBackward(int repcnt, long delta) {
if (mService == null)
return;
try {
if (repcnt == 0) {
mStartSeekPos = mService.position();
mLastSeekEventTime = 0;
mSeeking = false;
} else {
mSeeking = true;
if (delta < 5000) {
// seek at 10x speed for the first 5 seconds
delta = delta * 10;
} else {
// seek at 40x after that
delta = 50000 + (delta - 5000) * 40;
}
long newpos = mStartSeekPos - delta;
if (newpos < 0) {
// move to previous track
mService.prev();
long duration = mService.duration();
mStartSeekPos += duration;
newpos += duration;
}
if (((delta - mLastSeekEventTime) > 250) || repcnt < 0)
{
mService.seek(newpos);
mLastSeekEventTime = delta;
}
if (repcnt >= 0) {
mPosOverride = newpos;
} else {
mPosOverride = -1;
}
refreshNow();
}
} catch (RemoteException ex) {
}
}
private void scanForward(int repcnt, long delta) {
if (mService == null)
return;
try {
if (repcnt == 0) {
mStartSeekPos = mService.position();
mLastSeekEventTime = 0;
mSeeking = false;
} else {
mSeeking = true;
if (delta < 5000) {
// seek at 10x speed for the first 5 seconds
delta = delta * 10;
} else {
// seek at 40x after that
delta = 50000 + (delta - 5000) * 40;
}
long newpos = mStartSeekPos + delta;
long duration = mService.duration();
if (newpos >= duration) {
// move to next track
mService.next();
mStartSeekPos -= duration; // is OK to go
negative
newpos -= duration;
}
if (((delta - mLastSeekEventTime) > 250) || repcnt < 0) {
mService.seek(newpos);
mLastSeekEventTime = delta;
}
if (repcnt >= 0) {
mPosOverride = newpos;
} else {
mPosOverride = -1;
}
refreshNow();
}
} catch (RemoteException ex) {
}
}
private void doPauseResume() {
try {
if (mService != null) {
if (mService.isPlaying()) {
mService.pause();
} else {
mService.play();
}
updateTrackInfo();
refreshNow();
setPauseButtonImage();
}
} catch (RemoteException ex) {
}
}
private void toggleShuffle() {
if (mService == null) {
return;
}
try {
int shuffle = mService.getShuffleMode();
if (shuffle == MediaPlaybackService.SHUFFLE_NONE) {
mService.setShuffleMode(MediaPlaybackService.SHUFFLE_NORMAL);
if (mService.getRepeatMode() ==
MediaPlaybackService.REPEAT_CURRENT) {
mService.setRepeatMode(MediaPlaybackService.REPEAT_ALL);
}
showToast(R.string.shuffle_on_notif);
} else if (shuffle == MediaPlaybackService.SHUFFLE_NORMAL
|| shuffle == MediaPlaybackService.SHUFFLE_AUTO) {
mService.setShuffleMode(MediaPlaybackService.SHUFFLE_NONE);
showToast(R.string.shuffle_off_notif);
} else {
Log.e("MediaPlaybackActivity", "Invalid shuffle mode: "
+ shuffle);
}
} catch (RemoteException ex) {
}
}
private void cycleRepeat() {
if (mService == null) {
return;
}
try {
int mode = mService.getRepeatMode();
if (mode == MediaPlaybackService.REPEAT_NONE) {
mService.setRepeatMode(MediaPlaybackService.REPEAT_ALL);
showToast(R.string.repeat_all_notif);
} else if (mode == MediaPlaybackService.REPEAT_ALL) {
mService.setRepeatMode(MediaPlaybackService.REPEAT_CURRENT);
if (mService.getShuffleMode() != MediaPlaybackService.SHUFFLE_NONE) {
mService.setShuffleMode(MediaPlaybackService.SHUFFLE_NONE);
}
showToast(R.string.repeat_current_notif);
} else {
mService.setRepeatMode(MediaPlaybackService.REPEAT_NONE);
showToast(R.string.repeat_off_notif);
}
} catch (RemoteException ex) {
}
}
private void showToast(int resid) {
if (mToast == null) {
mToast = Toast.makeText(this, "", Toast.LENGTH_SHORT);
}
mToast.setText(resid);
mToast.show();
}
private void startPlayback() {
if (mService == null)
return;
Intent intent = getIntent();
String filename = "";
Uri uri = intent.getData();
if (uri != null && uri.toString().length() > 0) {
// If this is a file:// URI, just use the path directly instead
// of going through the open-from-filedescriptor codepath.
String scheme = uri.getScheme();
if ("file".equals(scheme)) {
filename = uri.getPath();
} else {
filename = uri.toString();
}
try {
if (!ContentResolver.SCHEME_CONTENT.equals(scheme)
|| !MediaStore.AUTHORITY.equals(uri.getAuthority())) {
mOneShot = true;
}
mService.stop();
mService.openFile(filename, mOneShot);
mService.play();
setIntent(new Intent());
} catch (Exception ex) {
Log.d("MediaPlaybackActivity", "couldn't start playback: " + ex);
}
}
updateTrackInfo();
long next = refreshNow();
queueNextRefresh(next);
}
private ServiceConnection osc = new ServiceConnection() {
public void onServiceConnected(ComponentName classname, IBinder obj) {
mService = IMediaPlaybackService.Stub.asInterface(obj);
startPlayback();
try {
// Assume something is playing when the service says it is,
// but also if the audio ID is valid but the service is paused.
if (mService.getAudioId() >= 0 || mService.isPlaying()
|| mService.getPath() != null) {
// something is playing now, we're done
setPauseButtonImage();
return;
}
} catch (RemoteException ex) {
}
// Service is dead or not playing anything. If we got here as part
// of a "play this file" Intent, exit. Otherwise go to the Music
// app start screen.
if (getIntent().getData() == null) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClass(BaseClass.this, MusicBrowserActivity.class);
startActivity(intent);
}
finish();
}
public void onServiceDisconnected(ComponentName classname) {
mService = null;
}
};
private void setPauseButtonImage() {
try {
if (mService != null && mService.isPlaying()) {
mPauseButton
.setImageResource(android.R.drawable.ic_media_pause);
} else {
mPauseButton.setImageResource(android.R.drawable.ic_media_play);
}
} catch (RemoteException ex) {
}
}
private TextView mCurrentTime;
private TextView mTotalTime;
private long mPosOverride = -1;
private boolean mFromTouch = false;
private long mDuration;
private int seekmethod;
private boolean paused;
private static final int REFRESH = 1;
private static final int QUIT = 2;
private static final int GET_ALBUM_ART = 3;
private static final int ALBUM_ART_DECODED = 4;
private void queueNextRefresh(long delay) {
if (!paused) {
Message msg = mHandler.obtainMessage(REFRESH);
mHandler.removeMessages(REFRESH);
mHandler.sendMessageDelayed(msg, delay);
}
}
private long refreshNow() {
if (mService == null)
return 500;
try {
long pos = mPosOverride < 0 ? mService.position() : mPosOverride;
long remaining = 1000 - (pos % 1000);
if ((pos >= 0) && (mService.duration() > 0)) {
mCurrentTime.setText(MusicUtils
.makeTimeString(this, pos / 1000));
if (mService.isPlaying()) {
mCurrentTime.setVisibility(View.VISIBLE);
} else {
// blink the counter
int vis = mCurrentTime.getVisibility();
mCurrentTime
.setVisibility(vis == View.INVISIBLE ? View.VISIBLE
: View.INVISIBLE);
remaining = 500;
}
} else {
mCurrentTime.setText("--:--");
}
// return the number of milliseconds until the next full second, so
// the counter can be updated at just the right time
return remaining;
} catch (RemoteException ex) {
}
return 500;
}
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case REFRESH:
updateTrackInfo();
long next = refreshNow();
queueNextRefresh(next);
break;
case QUIT:
// This can be moved back to onCreate once the bug that prevents
// Dialogs from being started from onCreate/onResume is fixed.
new AlertDialog.Builder(BaseClass.this)
.setTitle(R.string.service_start_error_title)
.setMessage(R.string.service_start_error_msg)
.setPositiveButton(R.string.service_start_error_button,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int whichButton) {
finish();
}
}).setCancelable(false).show();
break;
default:
break;
}
}
};
private BroadcastReceiver mStatusListener = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(MediaPlaybackService.META_CHANGED)) {
// redraw the artist/title info and
// set new max for progress bar
updateTrackInfo();
setPauseButtonImage();
queueNextRefresh(1);
} else if (action.equals(MediaPlaybackService.PLAYBACK_COMPLETE)) {
if (mOneShot) {
finish();
} else {
setPauseButtonImage();
}
} else if (action.equals(MediaPlaybackService.PLAYSTATE_CHANGED)) {
setPauseButtonImage();
}
}
};
private static class AlbumSongIdWrapper {
public long albumid;
public long songid;
AlbumSongIdWrapper(long aid, long sid) {
albumid = aid;
songid = sid;
}
}
private void updateTrackInfo() {
if (mService == null) {
return;
}
try {
String path = mService.getPath();
if (path == null) {
finish();
return;
}
long songid = mService.getAudioId();
if (songid < 0 && path.toLowerCase().startsWith("http://")) {
// Once we can get album art and meta data from MediaPlayer, we
// can show that info again when streaming.
mAlbumArtHandler.removeMessages(GET_ALBUM_ART);
mAlbumArtHandler.obtainMessage(GET_ALBUM_ART,
new AlbumSongIdWrapper(-1, -1)).sendToTarget();
} else {
String artistName = mService.getArtistName();
if (MediaStore.UNKNOWN_STRING.equals(artistName)) {
artistName = getString(R.string.unknown_artist_name);
}
String albumName = mService.getAlbumName();
long albumid = mService.getAlbumId();
if (MediaStore.UNKNOWN_STRING.equals(albumName)) {
albumName = getString(R.string.unknown_album_name);
albumid = -1;
}
mAlbumArtHandler.removeMessages(GET_ALBUM_ART);
mAlbumArtHandler.obtainMessage(GET_ALBUM_ART,
new AlbumSongIdWrapper(albumid, songid)).sendToTarget();
}
mDuration = mService.duration();
mTotalTime.setText(MusicUtils
.makeTimeString(this, mDuration / 1000));
} catch (RemoteException ex) {
finish();
}
}
public class AlbumArtHandler extends Handler {
private long mAlbumId = -1;
public AlbumArtHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
long albumid = ((AlbumSongIdWrapper) msg.obj).albumid;
long songid = ((AlbumSongIdWrapper) msg.obj).songid;
if (msg.what == GET_ALBUM_ART
&& (mAlbumId != albumid || albumid < 0)) {
// while decoding the new image, show the default album art
Message numsg = mHandler.obtainMessage(ALBUM_ART_DECODED, null);
mHandler.removeMessages(ALBUM_ART_DECODED);
mHandler.sendMessageDelayed(numsg, 300);
Bitmap bm = MusicUtils.getArtwork(BaseClass.this, songid,
albumid);
if (bm == null) {
bm = MusicUtils.getArtwork(BaseClass.this, songid, -1);
albumid = -1;
}
if (bm != null) {
numsg = mHandler.obtainMessage(ALBUM_ART_DECODED, bm);
mHandler.removeMessages(ALBUM_ART_DECODED);
mHandler.sendMessage(numsg);
}
mAlbumId = albumid;
}
}
}
public static class Worker implements Runnable {
private final Object mLock = new Object();
private Looper mLooper;
/**
* Creates a worker thread with the given name. The thread then runs a
* {@link android.os.Looper}.
*
* @param name
* A name for the new thread
*/
Worker(String name) {
Thread t = new Thread(null, this, name);
t.setPriority(Thread.MIN_PRIORITY);
t.start();
synchronized (mLock) {
while (mLooper == null) {
try {
mLock.wait();
} catch (InterruptedException ex) {
}
}
}
}
public Looper getLooper() {
return mLooper;
}
public void run() {
synchronized (mLock) {
Looper.prepare();
mLooper = Looper.myLooper();
mLock.notifyAll();
}
Looper.loop();
}
public void quit() {
mLooper.quit();
}
}
}
そして、このクラスを拡張したいのですが、プログラムがクラッシュし、この基本クラスでnullポインター例外が表示されます。基本クラスにはコンテンツビューがなく、ボタン(前のボタンと次のボタン)を指し、次のように表示されます。 nullポインタ..この問題を解決するために私を案内してください。