/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.music;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.BroadcastReceiver;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.graphics.Bitmap;
import android.media.audiofx.AudioEffect;
import android.media.AudioManager;
import android.media.AudioManager.OnAudioFocusChangeListener;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.media.RemoteControlClient;
import android.media.RemoteControlClient.MetadataEditor;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.PowerManager.WakeLock;
import android.provider.MediaStore;
import android.util.Log;
import android.widget.RemoteViews;
import android.widget.Toast;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.Random;
import java.util.Vector;
/**
* Provides "background" audio playback capabilities, allowing the
* user to switch between activities without stopping playback.
*/
public class MediaPlaybackService extends Service {
/** used to specify whether enqueue() should start playing
* the new list of files right away, next or once all the currently
* queued files have been played
*/
public static final int NOW = 1;
public static final int NEXT = 2;
public static final int LAST = 3;
public static final int PLAYBACKSERVICE_STATUS = 1;
public static final int SHUFFLE_NONE = 0;
public static final int SHUFFLE_NORMAL = 1;
public static final int SHUFFLE_AUTO = 2;
public static final int REPEAT_NONE = 0;
public static final int REPEAT_CURRENT = 1;
public static final int REPEAT_ALL = 2;
public static final String PLAYSTATE_CHANGED = "com.android.music.playstatechanged";
public static final String META_CHANGED = "com.android.music.metachanged";
public static final String QUEUE_CHANGED = "com.android.music.queuechanged";
public static final String SERVICECMD = "com.android.music.musicservicecommand";
public static final String CMDNAME = "command";
public static final String CMDTOGGLEPAUSE = "togglepause";
public static final String CMDSTOP = "stop";
public static final String CMDPAUSE = "pause";
public static final String CMDPLAY = "play";
public static final String CMDPREVIOUS = "previous";
public static final String CMDNEXT = "next";
public static final String TOGGLEPAUSE_ACTION = "com.android.music.musicservicecommand.togglepause";
public static final String PAUSE_ACTION = "com.android.music.musicservicecommand.pause";
public static final String PREVIOUS_ACTION = "com.android.music.musicservicecommand.previous";
public static final String NEXT_ACTION = "com.android.music.musicservicecommand.next";
private static final int TRACK_ENDED = 1;
private static final int RELEASE_WAKELOCK = 2;
private static final int SERVER_DIED = 3;
private static final int FOCUSCHANGE = 4;
private static final int FADEDOWN = 5;
private static final int FADEUP = 6;
private static final int MAX_HISTORY_SIZE = 100;
private MultiPlayer mPlayer;
private String mFileToPlay;
private int mShuffleMode = SHUFFLE_NONE;
private int mRepeatMode = REPEAT_NONE;
private int mMediaMountedCount = 0;
private long [] mAutoShuffleList = null;
private long [] mPlayList = null;
private int mPlayListLen = 0;
private Vector<Integer> mHistory = new Vector<Integer>(MAX_HISTORY_SIZE);
private Cursor mCursor;
private int mPlayPos = -1;
private static final String LOGTAG = "MediaPlaybackService";
private final Shuffler mRand = new Shuffler();
private int mOpenFailedCounter = 0;
String[] mCursorCols = new String[] {
"audio._id AS _id", // index must match IDCOLIDX below
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.MIME_TYPE,
MediaStore.Audio.Media.ALBUM_ID,
MediaStore.Audio.Media.ARTIST_ID,
MediaStore.Audio.Media.IS_PODCAST, // index must match PODCASTCOLIDX below
MediaStore.Audio.Media.BOOKMARK // index must match BOOKMARKCOLIDX below
};
private final static int IDCOLIDX = 0;
private final static int PODCASTCOLIDX = 8;
private final static int BOOKMARKCOLIDX = 9;
private BroadcastReceiver mUnmountReceiver = null;
private WakeLock mWakeLock;
private int mServiceStartId = -1;
private boolean mServiceInUse = false;
private boolean mIsSupposedToBePlaying = false;
private boolean mQuietMode = false;
private AudioManager mAudioManager;
private boolean mQueueIsSaveable = true;
// used to track what type of audio focus loss caused the playback to pause
private boolean mPausedByTransientLossOfFocus = false;
private SharedPreferences mPreferences;
// We use this to distinguish between different cards when saving/restoring playlists.
// This will have to change if we want to support multiple simultaneous cards.
private int mCardId;
private MediaAppWidgetProvider mAppWidgetProvider = MediaAppWidgetProvider.getInstance();
// interval after which we stop the service when idle
private static final int IDLE_DELAY = 60000;
private RemoteControlClient mRemoteControlClient;
private Handler mMediaplayerHandler = new Handler() {
float mCurrentVolume = 1.0f;
@Override
public void handleMessage(Message msg) {
MusicUtils.debugLog("mMediaplayerHandler.handleMessage " + msg.what);
switch (msg.what) {
case FADEDOWN:
mCurrentVolume -= .05f;
if (mCurrentVolume > .2f) {
mMediaplayerHandler.sendEmptyMessageDelayed(FADEDOWN, 10);
} else {
mCurrentVolume = .2f;
}
mPlayer.setVolume(mCurrentVolume);
break;
case FADEUP:
mCurrentVolume += .01f;
if (mCurrentVolume < 1.0f) {
mMediaplayerHandler.sendEmptyMessageDelayed(FADEUP, 10);
} else {
mCurrentVolume = 1.0f;
}
mPlayer.setVolume(mCurrentVolume);
break;
case SERVER_DIED:
if (mIsSupposedToBePlaying) {
next(true);
} else {
// the server died when we were idle, so just
// r