1792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project/*
2792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
3792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project *
4792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * you may not use this file except in compliance with the License.
6792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * You may obtain a copy of the License at
7792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project *
8792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project *
10792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * See the License for the specific language governing permissions and
14792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * limitations under the License.
15792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project */
16792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
17792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectpackage com.android.music;
18792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
19792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.app.Notification;
20792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.app.PendingIntent;
21792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.app.Service;
22490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Projectimport android.appwidget.AppWidgetManager;
233d22fc22dd66be08df07c86edd533dfe3d06efc1Jean-Michel Triviimport android.content.ComponentName;
24792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.content.ContentResolver;
25792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.content.ContentUris;
26792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.content.ContentValues;
27792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.content.Context;
28792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.content.Intent;
29792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.content.IntentFilter;
30792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.content.BroadcastReceiver;
31792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.content.SharedPreferences;
32792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.content.SharedPreferences.Editor;
33792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.database.Cursor;
34792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.database.sqlite.SQLiteException;
358717d3490e179e9591eac98a91a14995d98291adMarco Nelissenimport android.graphics.Bitmap;
36f2ef3b5153fd2f447939f267d8887ff3bba0d13fMarco Nelissenimport android.media.audiofx.AudioEffect;
37792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.media.AudioManager;
38085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Triviimport android.media.AudioManager.OnAudioFocusChangeListener;
398717d3490e179e9591eac98a91a14995d98291adMarco Nelissenimport android.media.MediaMetadataRetriever;
40792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.media.MediaPlayer;
41308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissenimport android.media.MediaPlayer.OnCompletionListener;
428717d3490e179e9591eac98a91a14995d98291adMarco Nelissenimport android.media.RemoteControlClient;
438717d3490e179e9591eac98a91a14995d98291adMarco Nelissenimport android.media.RemoteControlClient.MetadataEditor;
44792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.net.Uri;
45792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.os.Handler;
46792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.os.IBinder;
47792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.os.Message;
48792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.os.PowerManager;
49792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.os.SystemClock;
50792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.os.PowerManager.WakeLock;
51792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.provider.MediaStore;
52792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.util.Log;
53792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.widget.RemoteViews;
54792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.widget.Toast;
55792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
563988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissenimport java.io.FileDescriptor;
57792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport java.io.IOException;
583988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissenimport java.io.PrintWriter;
592b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissenimport java.lang.ref.WeakReference;
60792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport java.util.Random;
61792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport java.util.Vector;
62792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
63792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project/**
64792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * Provides "background" audio playback capabilities, allowing the
65792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * user to switch between activities without stopping playback.
66792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project */
67792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectpublic class MediaPlaybackService extends Service {
68792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /** used to specify whether enqueue() should start playing
69792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * the new list of files right away, next or once all the currently
70792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * queued files have been played
71792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
72792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final int NOW = 1;
73792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final int NEXT = 2;
74792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final int LAST = 3;
75792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final int PLAYBACKSERVICE_STATUS = 1;
76792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
77792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final int SHUFFLE_NONE = 0;
78792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final int SHUFFLE_NORMAL = 1;
79792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final int SHUFFLE_AUTO = 2;
80792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
81792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final int REPEAT_NONE = 0;
82792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final int REPEAT_CURRENT = 1;
83792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final int REPEAT_ALL = 2;
84792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
85792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final String PLAYSTATE_CHANGED = "com.android.music.playstatechanged";
86792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final String META_CHANGED = "com.android.music.metachanged";
87792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final String QUEUE_CHANGED = "com.android.music.queuechanged";
88792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
89792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final String SERVICECMD = "com.android.music.musicservicecommand";
90792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final String CMDNAME = "command";
91792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final String CMDTOGGLEPAUSE = "togglepause";
92792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final String CMDSTOP = "stop";
93792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final String CMDPAUSE = "pause";
94fc1c0dc301b5cae2e8c5661e7a217dc57dacac1eMarco Nelissen    public static final String CMDPLAY = "play";
95792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final String CMDPREVIOUS = "previous";
96792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final String CMDNEXT = "next";
97792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
98792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final String TOGGLEPAUSE_ACTION = "com.android.music.musicservicecommand.togglepause";
99792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final String PAUSE_ACTION = "com.android.music.musicservicecommand.pause";
100792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final String PREVIOUS_ACTION = "com.android.music.musicservicecommand.previous";
101792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final String NEXT_ACTION = "com.android.music.musicservicecommand.next";
102792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
103792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private static final int TRACK_ENDED = 1;
104792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private static final int RELEASE_WAKELOCK = 2;
105792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private static final int SERVER_DIED = 3;
1067181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen    private static final int FOCUSCHANGE = 4;
1072da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen    private static final int FADEDOWN = 5;
1082da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen    private static final int FADEUP = 6;
109e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen    private static final int TRACK_WENT_TO_NEXT = 7;
1103ec2ad9c8758d300864be808e491ae933dc9cd6bMarco Nelissen    private static final int MAX_HISTORY_SIZE = 100;
111792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
112792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private MultiPlayer mPlayer;
113792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private String mFileToPlay;
114792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private int mShuffleMode = SHUFFLE_NONE;
115792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private int mRepeatMode = REPEAT_NONE;
116792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private int mMediaMountedCount = 0;
117bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen    private long [] mAutoShuffleList = null;
118bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen    private long [] mPlayList = null;
119792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private int mPlayListLen = 0;
120792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private Vector<Integer> mHistory = new Vector<Integer>(MAX_HISTORY_SIZE);
121792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private Cursor mCursor;
122792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private int mPlayPos = -1;
123e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen    private int mNextPlayPos = -1;
124792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private static final String LOGTAG = "MediaPlaybackService";
125792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private final Shuffler mRand = new Shuffler();
126792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private int mOpenFailedCounter = 0;
127792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    String[] mCursorCols = new String[] {
128792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            "audio._id AS _id",             // index must match IDCOLIDX below
129792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            MediaStore.Audio.Media.ARTIST,
130792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            MediaStore.Audio.Media.ALBUM,
131792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            MediaStore.Audio.Media.TITLE,
132792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            MediaStore.Audio.Media.DATA,
133792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            MediaStore.Audio.Media.MIME_TYPE,
134792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            MediaStore.Audio.Media.ALBUM_ID,
135792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            MediaStore.Audio.Media.ARTIST_ID,
136792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            MediaStore.Audio.Media.IS_PODCAST, // index must match PODCASTCOLIDX below
137792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            MediaStore.Audio.Media.BOOKMARK    // index must match BOOKMARKCOLIDX below
138792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    };
139792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private final static int IDCOLIDX = 0;
140792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private final static int PODCASTCOLIDX = 8;
141792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private final static int BOOKMARKCOLIDX = 9;
142792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private BroadcastReceiver mUnmountReceiver = null;
143792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private WakeLock mWakeLock;
144792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private int mServiceStartId = -1;
145792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private boolean mServiceInUse = false;
146c1333370c94dd708f2d1555d8aee6ddf2bc88a37Marco Nelissen    private boolean mIsSupposedToBePlaying = false;
147792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private boolean mQuietMode = false;
148085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi    private AudioManager mAudioManager;
1498d08ec235831d71fdd7f7b6f7757c2bc19528faeMarco Nelissen    private boolean mQueueIsSaveable = true;
150085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi    // used to track what type of audio focus loss caused the playback to pause
151085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi    private boolean mPausedByTransientLossOfFocus = false;
152085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi
153792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private SharedPreferences mPreferences;
154792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    // We use this to distinguish between different cards when saving/restoring playlists.
155792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    // This will have to change if we want to support multiple simultaneous cards.
156792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private int mCardId;
157792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
158490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project    private MediaAppWidgetProvider mAppWidgetProvider = MediaAppWidgetProvider.getInstance();
159792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
160792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    // interval after which we stop the service when idle
161aa331877dd1a0777042481e5c925cd951682588fJean-Michel Trivi    private static final int IDLE_DELAY = 60000;
1628717d3490e179e9591eac98a91a14995d98291adMarco Nelissen
1638717d3490e179e9591eac98a91a14995d98291adMarco Nelissen    private RemoteControlClient mRemoteControlClient;
1648717d3490e179e9591eac98a91a14995d98291adMarco Nelissen
165792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private Handler mMediaplayerHandler = new Handler() {
166792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        float mCurrentVolume = 1.0f;
167792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        @Override
168792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void handleMessage(Message msg) {
1693988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen            MusicUtils.debugLog("mMediaplayerHandler.handleMessage " + msg.what);
170792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            switch (msg.what) {
1712da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                case FADEDOWN:
1722da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                    mCurrentVolume -= .05f;
1732da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                    if (mCurrentVolume > .2f) {
1742da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                        mMediaplayerHandler.sendEmptyMessageDelayed(FADEDOWN, 10);
175792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    } else {
1762da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                        mCurrentVolume = .2f;
1772da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                    }
1782da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                    mPlayer.setVolume(mCurrentVolume);
1792da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                    break;
1802da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                case FADEUP:
1812da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                    mCurrentVolume += .01f;
1822da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                    if (mCurrentVolume < 1.0f) {
1832da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                        mMediaplayerHandler.sendEmptyMessageDelayed(FADEUP, 10);
1842da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                    } else {
1852da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                        mCurrentVolume = 1.0f;
186792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    }
1872da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                    mPlayer.setVolume(mCurrentVolume);
188792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    break;
189792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                case SERVER_DIED:
190c1333370c94dd708f2d1555d8aee6ddf2bc88a37Marco Nelissen                    if (mIsSupposedToBePlaying) {
191bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                        gotoNext(true);
192792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    } else {
193792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        // the server died when we were idle, so just
194792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        // reopen the same song (it will start again
195792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        // from the beginning though when the user
196792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        // restarts)
197e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                        openCurrentAndNext();
198792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    }
199792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    break;
200e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                case TRACK_WENT_TO_NEXT:
201e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    mPlayPos = mNextPlayPos;
202e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    if (mCursor != null) {
203e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                        mCursor.close();
204e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                        mCursor = null;
205e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    }
206ea5075144c4f6da8dfd743ba1deb4ec7c59ef1c0kenthin                    if (mPlayPos >= 0 && mPlayPos < mPlayList.length) {
207ea5075144c4f6da8dfd743ba1deb4ec7c59ef1c0kenthin                        mCursor = getCursorForId(mPlayList[mPlayPos]);
208ea5075144c4f6da8dfd743ba1deb4ec7c59ef1c0kenthin                    }
209e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    notifyChange(META_CHANGED);
210e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    updateNotification();
211e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    setNextTrack();
212e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    break;
213792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                case TRACK_ENDED:
214792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    if (mRepeatMode == REPEAT_CURRENT) {
215792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        seek(0);
216792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        play();
217792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    } else {
218bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                        gotoNext(false);
219792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    }
220792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    break;
221792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                case RELEASE_WAKELOCK:
222792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mWakeLock.release();
223792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    break;
2247181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen
2257181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                case FOCUSCHANGE:
2267181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                    // This code is here so we can better synchronize it with the code that
2277181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                    // handles fade-in
2287181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                    switch (msg.arg1) {
2297181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                        case AudioManager.AUDIOFOCUS_LOSS:
2307181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_LOSS");
2317181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            if(isPlaying()) {
2327181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                                mPausedByTransientLossOfFocus = false;
2337181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            }
2347181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            pause();
2357181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            break;
2367181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
2377bae28fb4233e3d028b0436fb28f783dea14927eMarco Nelissen                            mMediaplayerHandler.removeMessages(FADEUP);
2387bae28fb4233e3d028b0436fb28f783dea14927eMarco Nelissen                            mMediaplayerHandler.sendEmptyMessage(FADEDOWN);
2397bae28fb4233e3d028b0436fb28f783dea14927eMarco Nelissen                            break;
2407bae28fb4233e3d028b0436fb28f783dea14927eMarco Nelissen                        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
2417181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_LOSS_TRANSIENT");
2427181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            if(isPlaying()) {
2437181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                                mPausedByTransientLossOfFocus = true;
2447181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            }
2457181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            pause();
2467181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            break;
2477181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                        case AudioManager.AUDIOFOCUS_GAIN:
2487181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_GAIN");
2497181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            if(!isPlaying() && mPausedByTransientLossOfFocus) {
2507181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                                mPausedByTransientLossOfFocus = false;
2517181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                                mCurrentVolume = 0f;
2527181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                                mPlayer.setVolume(mCurrentVolume);
2537181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                                play(); // also queues a fade-in
2547181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            } else {
2557bae28fb4233e3d028b0436fb28f783dea14927eMarco Nelissen                                mMediaplayerHandler.removeMessages(FADEDOWN);
2567181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                                mMediaplayerHandler.sendEmptyMessage(FADEUP);
2577181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            }
2587181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            break;
2597181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                        default:
2607181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            Log.e(LOGTAG, "Unknown audio focus change code");
2617181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                    }
2627181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                    break;
2637181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen
264792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                default:
265792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    break;
266792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
267792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
268792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    };
269792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
270792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
271792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        @Override
272792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void onReceive(Context context, Intent intent) {
273792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            String action = intent.getAction();
274792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            String cmd = intent.getStringExtra("command");
2753988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen            MusicUtils.debugLog("mIntentReceiver.onReceive " + action + " / " + cmd);
276792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (CMDNEXT.equals(cmd) || NEXT_ACTION.equals(action)) {
277bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                gotoNext(true);
278792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } else if (CMDPREVIOUS.equals(cmd) || PREVIOUS_ACTION.equals(action)) {
279792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                prev();
280792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } else if (CMDTOGGLEPAUSE.equals(cmd) || TOGGLEPAUSE_ACTION.equals(action)) {
281792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (isPlaying()) {
282792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    pause();
283aa331877dd1a0777042481e5c925cd951682588fJean-Michel Trivi                    mPausedByTransientLossOfFocus = false;
284792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } else {
285792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    play();
286792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
287792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } else if (CMDPAUSE.equals(cmd) || PAUSE_ACTION.equals(action)) {
288792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                pause();
289aa331877dd1a0777042481e5c925cd951682588fJean-Michel Trivi                mPausedByTransientLossOfFocus = false;
290fc1c0dc301b5cae2e8c5661e7a217dc57dacac1eMarco Nelissen            } else if (CMDPLAY.equals(cmd)) {
291fc1c0dc301b5cae2e8c5661e7a217dc57dacac1eMarco Nelissen                play();
292792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } else if (CMDSTOP.equals(cmd)) {
293792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                pause();
294aa331877dd1a0777042481e5c925cd951682588fJean-Michel Trivi                mPausedByTransientLossOfFocus = false;
295792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                seek(0);
296490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project            } else if (MediaAppWidgetProvider.CMDAPPWIDGETUPDATE.equals(cmd)) {
297490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project                // Someone asked us to refresh a set of specific widgets, probably
298792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                // because they were just added.
299490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project                int[] appWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
300490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project                mAppWidgetProvider.performUpdate(MediaPlaybackService.this, appWidgetIds);
301792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
302792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
303792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    };
304792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
305085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi    private OnAudioFocusChangeListener mAudioFocusListener = new OnAudioFocusChangeListener() {
306f4cfdfd4a99040be44a1ccd74edbde762edc845bJean-Michel Trivi        public void onAudioFocusChange(int focusChange) {
3077181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen            mMediaplayerHandler.obtainMessage(FOCUSCHANGE, focusChange, 0).sendToTarget();
308085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi        }
309085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi    };
310085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi
311792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public MediaPlaybackService() {
312792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
313792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
314792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    @Override
315792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void onCreate() {
316792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        super.onCreate();
317085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi
318085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
3198717d3490e179e9591eac98a91a14995d98291adMarco Nelissen        ComponentName rec = new ComponentName(getPackageName(),
3208717d3490e179e9591eac98a91a14995d98291adMarco Nelissen                MediaButtonIntentReceiver.class.getName());
3218717d3490e179e9591eac98a91a14995d98291adMarco Nelissen        mAudioManager.registerMediaButtonEventReceiver(rec);
3228ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood
3238ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood        Intent i = new Intent(Intent.ACTION_MEDIA_BUTTON);
3248ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood        i.setComponent(rec);
3258ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood        PendingIntent pi = PendingIntent.getBroadcast(this /*context*/,
3268ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood                0 /*requestCode, ignored*/, i /*intent*/, 0 /*flags*/);
3278ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood        mRemoteControlClient = new RemoteControlClient(pi);
3288ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood        mAudioManager.registerRemoteControlClient(mRemoteControlClient);
3298ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood
3308ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood        int flags = RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS
3318ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood                | RemoteControlClient.FLAG_KEY_MEDIA_NEXT
3328ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood                | RemoteControlClient.FLAG_KEY_MEDIA_PLAY
3338ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood                | RemoteControlClient.FLAG_KEY_MEDIA_PAUSE
3348ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood                | RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE
3358ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood                | RemoteControlClient.FLAG_KEY_MEDIA_STOP;
3368ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood        mRemoteControlClient.setTransportControlFlags(flags);
337792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
338792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mPreferences = getSharedPreferences("Music", MODE_WORLD_READABLE | MODE_WORLD_WRITEABLE);
33987bbf3c40cd044c3daa9fad28fb94e8570fcae05Marco Nelissen        mCardId = MusicUtils.getCardId(this);
340792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
341792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        registerExternalStorageListener();
342792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
343792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // Needs to be done in this thread, since otherwise ApplicationContext.getPowerManager() crashes.
344792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mPlayer = new MultiPlayer();
345792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mPlayer.setHandler(mMediaplayerHandler);
346792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
347792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        reloadQueue();
3488717d3490e179e9591eac98a91a14995d98291adMarco Nelissen        notifyChange(QUEUE_CHANGED);
3498717d3490e179e9591eac98a91a14995d98291adMarco Nelissen        notifyChange(META_CHANGED);
3508717d3490e179e9591eac98a91a14995d98291adMarco Nelissen
351792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        IntentFilter commandFilter = new IntentFilter();
352792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        commandFilter.addAction(SERVICECMD);
353792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        commandFilter.addAction(TOGGLEPAUSE_ACTION);
354792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        commandFilter.addAction(PAUSE_ACTION);
355792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        commandFilter.addAction(NEXT_ACTION);
356792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        commandFilter.addAction(PREVIOUS_ACTION);
357792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        registerReceiver(mIntentReceiver, commandFilter);
358792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
359792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
360792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this.getClass().getName());
361792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mWakeLock.setReferenceCounted(false);
362792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
363792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // If the service was idle, but got killed before it stopped itself, the
364792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // system will relaunch it. Make sure it gets stopped again in that case.
365792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        Message msg = mDelayedStopHandler.obtainMessage();
366792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY);
367792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
368792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
369792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    @Override
370792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void onDestroy() {
371792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // Check that we're not being destroyed while something is still playing.
372792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (isPlaying()) {
373e99341f9674462607f3fd5e8dbd100b34b973e1bMarco Nelissen            Log.e(LOGTAG, "Service being destroyed while still playing.");
374792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
3752b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        // release all MediaPlayer resources, including the native player and wakelocks
376f2ef3b5153fd2f447939f267d8887ff3bba0d13fMarco Nelissen        Intent i = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
377f2ef3b5153fd2f447939f267d8887ff3bba0d13fMarco Nelissen        i.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, getAudioSessionId());
378f2ef3b5153fd2f447939f267d8887ff3bba0d13fMarco Nelissen        i.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName());
379f2ef3b5153fd2f447939f267d8887ff3bba0d13fMarco Nelissen        sendBroadcast(i);
3802b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        mPlayer.release();
3812b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        mPlayer = null;
382085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi
383085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi        mAudioManager.abandonAudioFocus(mAudioFocusListener);
3848ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood        mAudioManager.unregisterRemoteControlClient(mRemoteControlClient);
385792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
3862b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        // make sure there aren't any other messages coming
3872b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        mDelayedStopHandler.removeCallbacksAndMessages(null);
38849e36ea916aefba042c6e927c771b10708b6891eMarco Nelissen        mMediaplayerHandler.removeCallbacksAndMessages(null);
3892b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen
390792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mCursor != null) {
391792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mCursor.close();
392792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mCursor = null;
393792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
394792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
395792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        unregisterReceiver(mIntentReceiver);
396792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mUnmountReceiver != null) {
397792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            unregisterReceiver(mUnmountReceiver);
398792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mUnmountReceiver = null;
399792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
400792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mWakeLock.release();
401792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        super.onDestroy();
402792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
403792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
404792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private final char hexdigits [] = new char [] {
405792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            '0', '1', '2', '3',
406792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            '4', '5', '6', '7',
407792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            '8', '9', 'a', 'b',
408792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            'c', 'd', 'e', 'f'
409792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    };
410792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
411792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private void saveQueue(boolean full) {
4128d08ec235831d71fdd7f7b6f7757c2bc19528faeMarco Nelissen        if (!mQueueIsSaveable) {
413792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return;
414792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
4158d08ec235831d71fdd7f7b6f7757c2bc19528faeMarco Nelissen
416792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        Editor ed = mPreferences.edit();
417792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        //long start = System.currentTimeMillis();
418792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (full) {
419792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            StringBuilder q = new StringBuilder();
420792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
421792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // The current playlist is saved as a list of "reverse hexadecimal"
422792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // numbers, which we can generate faster than normal decimal or
423792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // hexadecimal numbers, which in turn allows us to save the playlist
424792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // more often without worrying too much about performance.
425792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // (saving the full state takes about 40 ms under no-load conditions
426792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // on the phone)
427792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            int len = mPlayListLen;
428792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            for (int i = 0; i < len; i++) {
429bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen                long n = mPlayList[i];
430133550210e448fe52698f6cd0e80e07b8cc480bdMarco Nelissen                if (n < 0) {
431133550210e448fe52698f6cd0e80e07b8cc480bdMarco Nelissen                    continue;
432133550210e448fe52698f6cd0e80e07b8cc480bdMarco Nelissen                } else if (n == 0) {
433792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    q.append("0;");
434792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } else {
435792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    while (n != 0) {
436bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen                        int digit = (int)(n & 0xf);
437133550210e448fe52698f6cd0e80e07b8cc480bdMarco Nelissen                        n >>>= 4;
438792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        q.append(hexdigits[digit]);
439792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    }
440792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    q.append(";");
441792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
442792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
443792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            //Log.i("@@@@ service", "created queue string in " + (System.currentTimeMillis() - start) + " ms");
444792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            ed.putString("queue", q.toString());
445792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            ed.putInt("cardid", mCardId);
44663dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen            if (mShuffleMode != SHUFFLE_NONE) {
44763dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                // In shuffle mode we need to save the history too
44863dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                len = mHistory.size();
44963dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                q.setLength(0);
45063dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                for (int i = 0; i < len; i++) {
45163dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                    int n = mHistory.get(i);
45263dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                    if (n == 0) {
45363dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                        q.append("0;");
45463dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                    } else {
45563dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                        while (n != 0) {
45663dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            int digit = (n & 0xf);
457133550210e448fe52698f6cd0e80e07b8cc480bdMarco Nelissen                            n >>>= 4;
45863dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            q.append(hexdigits[digit]);
45963dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                        }
46063dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                        q.append(";");
46163dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                    }
46263dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                }
46363dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                ed.putString("history", q.toString());
46463dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen            }
465792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
466792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        ed.putInt("curpos", mPlayPos);
467792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mPlayer.isInitialized()) {
468792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            ed.putLong("seekpos", mPlayer.position());
469792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
470792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        ed.putInt("repeatmode", mRepeatMode);
471792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        ed.putInt("shufflemode", mShuffleMode);
47214c3caeb7bc2de0dd7abbb1e5f217dbb6367afbaBrad Fitzpatrick        SharedPreferencesCompat.apply(ed);
47314c3caeb7bc2de0dd7abbb1e5f217dbb6367afbaBrad Fitzpatrick
474792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        //Log.i("@@@@ service", "saved state in " + (System.currentTimeMillis() - start) + " ms");
475792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
476792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
477792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private void reloadQueue() {
478792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        String q = null;
479792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
480792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        boolean newstyle = false;
481792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        int id = mCardId;
482792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mPreferences.contains("cardid")) {
483792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            newstyle = true;
484792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            id = mPreferences.getInt("cardid", ~mCardId);
485792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
486792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (id == mCardId) {
487792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // Only restore the saved playlist if the card is still
488792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // the same one as when the playlist was saved
489792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            q = mPreferences.getString("queue", "");
490792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
4916c615a208d88fe49c7da43c59720f9db6ca1e964Marco Nelissen        int qlen = q != null ? q.length() : 0;
4926c615a208d88fe49c7da43c59720f9db6ca1e964Marco Nelissen        if (qlen > 1) {
493792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            //Log.i("@@@@ service", "loaded queue: " + q);
494bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen            int plen = 0;
495bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen            int n = 0;
496bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen            int shift = 0;
497bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen            for (int i = 0; i < qlen; i++) {
498bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                char c = q.charAt(i);
499bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                if (c == ';') {
500bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                    ensurePlayListCapacity(plen + 1);
501bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                    mPlayList[plen] = n;
502bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                    plen++;
503bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                    n = 0;
504bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                    shift = 0;
505792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } else {
506bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                    if (c >= '0' && c <= '9') {
507bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                        n += ((c - '0') << shift);
508bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                    } else if (c >= 'a' && c <= 'f') {
509bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                        n += ((10 + c - 'a') << shift);
510bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                    } else {
511bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                        // bogus playlist data
512bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                        plen = 0;
513bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                        break;
514bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                    }
515bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                    shift += 4;
516792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
517792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
518bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen            mPlayListLen = plen;
519792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
520792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            int pos = mPreferences.getInt("curpos", 0);
521bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen            if (pos < 0 || pos >= mPlayListLen) {
522792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                // The saved playlist is bogus, discard it
523792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayListLen = 0;
524792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return;
525792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
526792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mPlayPos = pos;
527792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
528792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // When reloadQueue is called in response to a card-insertion,
529792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // we might not be able to query the media provider right away.
530792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // To deal with this, try querying for the current file, and if
531792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // that fails, wait a while and try again. If that too fails,
532792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // assume there is a problem and don't restore the state.
53363dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen            Cursor crsr = MusicUtils.query(this,
534792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
535792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        new String [] {"_id"}, "_id=" + mPlayList[mPlayPos] , null, null);
53663dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen            if (crsr == null || crsr.getCount() == 0) {
537792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                // wait a bit and try again
538792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                SystemClock.sleep(3000);
53963dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                crsr = getContentResolver().query(
540792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
541792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        mCursorCols, "_id=" + mPlayList[mPlayPos] , null, null);
542792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
54363dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen            if (crsr != null) {
54463dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                crsr.close();
545792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
546792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
547792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // Make sure we don't auto-skip to the next song, since that
548792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // also starts playback. What could happen in that case is:
549792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // - music is paused
550792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // - go to UMS and delete some files, including the currently playing one
551792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // - come back from UMS
552792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // (time passes)
553792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // - music app is killed for some reason (out of memory)
554792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // - music service is restarted, service restores state, doesn't find
555792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            //   the "current" file, goes to the next and: playback starts on its
556792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            //   own, potentially at some random inconvenient time.
557792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mOpenFailedCounter = 20;
558792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mQuietMode = true;
559e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            openCurrentAndNext();
560792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mQuietMode = false;
561792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (!mPlayer.isInitialized()) {
562792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                // couldn't restore the saved state
563792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayListLen = 0;
564792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return;
565792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
566792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
567792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            long seekpos = mPreferences.getLong("seekpos", 0);
568792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            seek(seekpos >= 0 && seekpos < duration() ? seekpos : 0);
569e99341f9674462607f3fd5e8dbd100b34b973e1bMarco Nelissen            Log.d(LOGTAG, "restored queue, currently at position "
570e99341f9674462607f3fd5e8dbd100b34b973e1bMarco Nelissen                    + position() + "/" + duration()
571e99341f9674462607f3fd5e8dbd100b34b973e1bMarco Nelissen                    + " (requested " + seekpos + ")");
572792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
573792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            int repmode = mPreferences.getInt("repeatmode", REPEAT_NONE);
574792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (repmode != REPEAT_ALL && repmode != REPEAT_CURRENT) {
575792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                repmode = REPEAT_NONE;
576792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
577792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mRepeatMode = repmode;
578792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
579792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            int shufmode = mPreferences.getInt("shufflemode", SHUFFLE_NONE);
580792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (shufmode != SHUFFLE_AUTO && shufmode != SHUFFLE_NORMAL) {
581792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                shufmode = SHUFFLE_NONE;
582792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
58363dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen            if (shufmode != SHUFFLE_NONE) {
58463dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                // in shuffle mode we need to restore the history too
58563dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                q = mPreferences.getString("history", "");
58663dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                qlen = q != null ? q.length() : 0;
58763dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                if (qlen > 1) {
58863dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                    plen = 0;
58963dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                    n = 0;
59063dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                    shift = 0;
59163dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                    mHistory.clear();
59263dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                    for (int i = 0; i < qlen; i++) {
59363dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                        char c = q.charAt(i);
59463dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                        if (c == ';') {
59563dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            if (n >= mPlayListLen) {
59663dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                                // bogus history data
59763dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                                mHistory.clear();
59863dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                                break;
59963dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            }
60063dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            mHistory.add(n);
60163dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            n = 0;
60263dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            shift = 0;
60363dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                        } else {
60463dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            if (c >= '0' && c <= '9') {
60563dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                                n += ((c - '0') << shift);
60663dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            } else if (c >= 'a' && c <= 'f') {
60763dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                                n += ((10 + c - 'a') << shift);
60863dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            } else {
60963dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                                // bogus history data
61063dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                                mHistory.clear();
61163dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                                break;
61263dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            }
61363dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            shift += 4;
61463dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                        }
61563dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                    }
61663dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                }
61763dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen            }
618792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (shufmode == SHUFFLE_AUTO) {
619792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (! makeAutoShuffleList()) {
620792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    shufmode = SHUFFLE_NONE;
621792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
622792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
623792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mShuffleMode = shufmode;
624792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
625792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
626792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
627792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    @Override
628792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public IBinder onBind(Intent intent) {
629792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mDelayedStopHandler.removeCallbacksAndMessages(null);
630792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mServiceInUse = true;
631792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return mBinder;
632792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
633792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
634792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    @Override
635792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void onRebind(Intent intent) {
636792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mDelayedStopHandler.removeCallbacksAndMessages(null);
637792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mServiceInUse = true;
638792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
639792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
640792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    @Override
641c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen    public int onStartCommand(Intent intent, int flags, int startId) {
642792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mServiceStartId = startId;
643792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mDelayedStopHandler.removeCallbacksAndMessages(null);
644c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen
645c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen        if (intent != null) {
646c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen            String action = intent.getAction();
647c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen            String cmd = intent.getStringExtra("command");
6483988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen            MusicUtils.debugLog("onStartCommand " + action + " / " + cmd);
649c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen
650c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen            if (CMDNEXT.equals(cmd) || NEXT_ACTION.equals(action)) {
651bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                gotoNext(true);
652c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen            } else if (CMDPREVIOUS.equals(cmd) || PREVIOUS_ACTION.equals(action)) {
653b63b5d1b6cae1cc9e28ba63b316fc15da5e16939Marco Nelissen                if (position() < 2000) {
654b63b5d1b6cae1cc9e28ba63b316fc15da5e16939Marco Nelissen                    prev();
655b63b5d1b6cae1cc9e28ba63b316fc15da5e16939Marco Nelissen                } else {
656b63b5d1b6cae1cc9e28ba63b316fc15da5e16939Marco Nelissen                    seek(0);
657b63b5d1b6cae1cc9e28ba63b316fc15da5e16939Marco Nelissen                    play();
658b63b5d1b6cae1cc9e28ba63b316fc15da5e16939Marco Nelissen                }
659c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen            } else if (CMDTOGGLEPAUSE.equals(cmd) || TOGGLEPAUSE_ACTION.equals(action)) {
660c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen                if (isPlaying()) {
661c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen                    pause();
662aa331877dd1a0777042481e5c925cd951682588fJean-Michel Trivi                    mPausedByTransientLossOfFocus = false;
663c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen                } else {
664c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen                    play();
665c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen                }
666c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen            } else if (CMDPAUSE.equals(cmd) || PAUSE_ACTION.equals(action)) {
667792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                pause();
668aa331877dd1a0777042481e5c925cd951682588fJean-Michel Trivi                mPausedByTransientLossOfFocus = false;
66947dd847ba35375d95c15bc1ee141d638f03ec82eJaikumar Ganesh            } else if (CMDPLAY.equals(cmd)) {
67047dd847ba35375d95c15bc1ee141d638f03ec82eJaikumar Ganesh                play();
671c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen            } else if (CMDSTOP.equals(cmd)) {
672c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen                pause();
673aa331877dd1a0777042481e5c925cd951682588fJean-Michel Trivi                mPausedByTransientLossOfFocus = false;
674c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen                seek(0);
675792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
676792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
677792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
678792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // make sure the service will shut down on its own if it was
679792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // just started but not bound to and nothing is playing
680792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mDelayedStopHandler.removeCallbacksAndMessages(null);
681792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        Message msg = mDelayedStopHandler.obtainMessage();
682792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY);
683c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen        return START_STICKY;
684792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
685792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
686792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    @Override
687792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public boolean onUnbind(Intent intent) {
688792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mServiceInUse = false;
689792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
690792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // Take a snapshot of the current playlist
691792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        saveQueue(true);
692792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
693aa331877dd1a0777042481e5c925cd951682588fJean-Michel Trivi        if (isPlaying() || mPausedByTransientLossOfFocus) {
694792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // something is currently playing, or will be playing once
695aa331877dd1a0777042481e5c925cd951682588fJean-Michel Trivi            // an in-progress action requesting audio focus ends, so don't stop the service now.
696792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return true;
697792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
698792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
699792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // If there is a playlist but playback is paused, then wait a while
700792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // before stopping the service, so that pause/resume isn't slow.
701792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // Also delay stopping the service if we're transitioning between tracks.
702792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mPlayListLen > 0  || mMediaplayerHandler.hasMessages(TRACK_ENDED)) {
703792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            Message msg = mDelayedStopHandler.obtainMessage();
704792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY);
705792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return true;
706792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
707792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
708792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // No active playlist, OK to stop the service right now
709792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        stopSelf(mServiceStartId);
710792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return true;
711792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
712792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
713792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private Handler mDelayedStopHandler = new Handler() {
714792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        @Override
715792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void handleMessage(Message msg) {
716792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // Check again to make sure nothing is playing right now
717aa331877dd1a0777042481e5c925cd951682588fJean-Michel Trivi            if (isPlaying() || mPausedByTransientLossOfFocus || mServiceInUse
718792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    || mMediaplayerHandler.hasMessages(TRACK_ENDED)) {
719792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return;
720792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
721792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // save the queue again, because it might have changed
722792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // since the user exited the music app (because of
723792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // party-shuffle or because the play-position changed)
724792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            saveQueue(true);
725792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            stopSelf(mServiceStartId);
726792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
727792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    };
728f2ef3b5153fd2f447939f267d8887ff3bba0d13fMarco Nelissen
729792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
730792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Called when we receive a ACTION_MEDIA_EJECT notification.
731792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     *
732792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param storagePath path to mount point for the removed media
733792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
734792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void closeExternalStorageFiles(String storagePath) {
735792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // stop playback and clean up if the SD card is going to be unmounted.
736792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        stop(true);
737792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        notifyChange(QUEUE_CHANGED);
738792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        notifyChange(META_CHANGED);
739792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
740792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
741792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
742792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Registers an intent to listen for ACTION_MEDIA_EJECT notifications.
743792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * The intent will call closeExternalStorageFiles() if the external media
744792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * is going to be ejected, so applications can clean up any files they have open.
745792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
746792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void registerExternalStorageListener() {
747792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mUnmountReceiver == null) {
748792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mUnmountReceiver = new BroadcastReceiver() {
749792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                @Override
750792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                public void onReceive(Context context, Intent intent) {
751792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    String action = intent.getAction();
752792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    if (action.equals(Intent.ACTION_MEDIA_EJECT)) {
753792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        saveQueue(true);
7548d08ec235831d71fdd7f7b6f7757c2bc19528faeMarco Nelissen                        mQueueIsSaveable = false;
755792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        closeExternalStorageFiles(intent.getData().getPath());
756792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    } else if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
757792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        mMediaMountedCount++;
75887bbf3c40cd044c3daa9fad28fb94e8570fcae05Marco Nelissen                        mCardId = MusicUtils.getCardId(MediaPlaybackService.this);
759792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        reloadQueue();
7608d08ec235831d71fdd7f7b6f7757c2bc19528faeMarco Nelissen                        mQueueIsSaveable = true;
761792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        notifyChange(QUEUE_CHANGED);
762792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        notifyChange(META_CHANGED);
763792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    }
764792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
765792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            };
766792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            IntentFilter iFilter = new IntentFilter();
767792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            iFilter.addAction(Intent.ACTION_MEDIA_EJECT);
768792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            iFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
769792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            iFilter.addDataScheme("file");
770792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            registerReceiver(mUnmountReceiver, iFilter);
771792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
772792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
773792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
774792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
775792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Notify the change-receivers that something has changed.
776792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * The intent that is sent contains the following data
777792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * for the currently playing track:
778792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * "id" - Integer: the database row ID
779792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * "artist" - String: the name of the artist
780792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * "album" - String: the name of the album
781792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * "track" - String: the name of the track
782792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * The intent has an action that is one of
783792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * "com.android.music.metachanged"
784792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * "com.android.music.queuechanged",
785792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * "com.android.music.playbackcomplete"
786792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * "com.android.music.playstatechanged"
787792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * respectively indicating that a new track has
788792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * started playing, that the playback queue has
789792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * changed, that playback has stopped because
790792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * the last file in the list has been played,
791792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * or that the play-state changed (paused/resumed).
792792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
793792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private void notifyChange(String what) {
7948717d3490e179e9591eac98a91a14995d98291adMarco Nelissen
795792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        Intent i = new Intent(what);
796bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen        i.putExtra("id", Long.valueOf(getAudioId()));
797792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        i.putExtra("artist", getArtistName());
798792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        i.putExtra("album",getAlbumName());
799792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        i.putExtra("track", getTrackName());
8006b507de7beda7f4a8b969b437dfbf81f5ecd5e5eMarco Nelissen        i.putExtra("playing", isPlaying());
8016b507de7beda7f4a8b969b437dfbf81f5ecd5e5eMarco Nelissen        sendStickyBroadcast(i);
8028717d3490e179e9591eac98a91a14995d98291adMarco Nelissen
8038717d3490e179e9591eac98a91a14995d98291adMarco Nelissen        if (what.equals(PLAYSTATE_CHANGED)) {
8048ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood            mRemoteControlClient.setPlaybackState(isPlaying() ?
8058ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood                    RemoteControlClient.PLAYSTATE_PLAYING : RemoteControlClient.PLAYSTATE_PAUSED);
8068717d3490e179e9591eac98a91a14995d98291adMarco Nelissen        } else if (what.equals(META_CHANGED)) {
8078ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood            RemoteControlClient.MetadataEditor ed = mRemoteControlClient.editMetadata(true);
8088ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood            ed.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, getTrackName());
8098ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood            ed.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, getAlbumName());
8108ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood            ed.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, getArtistName());
8118ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood            ed.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, duration());
8128ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood            Bitmap b = MusicUtils.getArtwork(this, getAudioId(), getAlbumId(), false);
8138ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood            if (b != null) {
8148ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood                ed.putBitmap(MetadataEditor.BITMAP_KEY_ARTWORK, b);
8158ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood            }
8168ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood            ed.apply();
8178717d3490e179e9591eac98a91a14995d98291adMarco Nelissen        }
8188717d3490e179e9591eac98a91a14995d98291adMarco Nelissen
819792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (what.equals(QUEUE_CHANGED)) {
820792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            saveQueue(true);
821792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        } else {
822792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            saveQueue(false);
823792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
824792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
825490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project        // Share this notification directly with our widgets
826490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project        mAppWidgetProvider.notifyChange(this, what);
827792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
828792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
829792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private void ensurePlayListCapacity(int size) {
830792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mPlayList == null || size > mPlayList.length) {
831792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // reallocate at 2x requested size so we don't
832792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // need to grow and copy the array for every
833792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // insert
834bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen            long [] newlist = new long[size * 2];
835bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen            int len = mPlayList != null ? mPlayList.length : mPlayListLen;
836792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            for (int i = 0; i < len; i++) {
837792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                newlist[i] = mPlayList[i];
838792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
839792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mPlayList = newlist;
840792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
841792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // FIXME: shrink the array when the needed size is much smaller
842792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // than the allocated size
843792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
844792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
845792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    // insert the list of songs at the specified position in the playlist
846bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen    private void addToPlayList(long [] list, int position) {
847792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        int addlen = list.length;
848792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (position < 0) { // overwrite
849792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mPlayListLen = 0;
850792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            position = 0;
851792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
852792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        ensurePlayListCapacity(mPlayListLen + addlen);
853792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (position > mPlayListLen) {
854792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            position = mPlayListLen;
855792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
856792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
857792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // move part of list after insertion point
858792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        int tailsize = mPlayListLen - position;
859792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        for (int i = tailsize ; i > 0 ; i--) {
860792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mPlayList[position + i] = mPlayList[position + i - addlen];
861792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
862792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
863792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // copy list into playlist
864792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        for (int i = 0; i < addlen; i++) {
865792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mPlayList[position + i] = list[i];
866792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
867792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mPlayListLen += addlen;
8683aa9ad029bc85da2e07bba019cec928ad5d74043Marco Nelissen        if (mPlayListLen == 0) {
8693aa9ad029bc85da2e07bba019cec928ad5d74043Marco Nelissen            mCursor.close();
8703aa9ad029bc85da2e07bba019cec928ad5d74043Marco Nelissen            mCursor = null;
8713aa9ad029bc85da2e07bba019cec928ad5d74043Marco Nelissen            notifyChange(META_CHANGED);
8723aa9ad029bc85da2e07bba019cec928ad5d74043Marco Nelissen        }
873792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
874792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
875792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
876792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Appends a list of tracks to the current playlist.
877792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * If nothing is playing currently, playback will be started at
878792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * the first track.
879792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * If the action is NOW, playback will switch to the first of
880792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * the new tracks immediately.
881792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param list The list of tracks to append.
882792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param action NOW, NEXT or LAST
883792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
884bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen    public void enqueue(long [] list, int action) {
885792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized(this) {
886792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (action == NEXT && mPlayPos + 1 < mPlayListLen) {
887792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                addToPlayList(list, mPlayPos + 1);
888792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                notifyChange(QUEUE_CHANGED);
889792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } else {
890792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                // action == LAST || action == NOW || mPlayPos + 1 == mPlayListLen
891792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                addToPlayList(list, Integer.MAX_VALUE);
892792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                notifyChange(QUEUE_CHANGED);
893792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (action == NOW) {
894792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mPlayPos = mPlayListLen - list.length;
895e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    openCurrentAndNext();
896792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    play();
897792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    notifyChange(META_CHANGED);
898792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    return;
899792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
900792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
901792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mPlayPos < 0) {
902792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayPos = 0;
903e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                openCurrentAndNext();
904792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                play();
905792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                notifyChange(META_CHANGED);
906792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
907792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
908792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
909792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
910792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
911792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Replaces the current playlist with a new list,
912792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * and prepares for starting playback at the specified
913792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * position in the list, or a random position if the
914792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * specified position is 0.
915792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param list The new list of tracks.
916792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
917bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen    public void open(long [] list, int position) {
918792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
919792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mShuffleMode == SHUFFLE_AUTO) {
920792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mShuffleMode = SHUFFLE_NORMAL;
921792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
922bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen            long oldId = getAudioId();
923792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            int listlength = list.length;
924792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            boolean newlist = true;
925792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mPlayListLen == listlength) {
926792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                // possible fast path: list might be the same
927792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                newlist = false;
928792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                for (int i = 0; i < listlength; i++) {
929792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    if (list[i] != mPlayList[i]) {
930792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        newlist = true;
931792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        break;
932792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    }
933792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
934792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
935792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (newlist) {
936792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                addToPlayList(list, -1);
937792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                notifyChange(QUEUE_CHANGED);
938792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
939792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            int oldpos = mPlayPos;
940792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (position >= 0) {
941792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayPos = position;
942792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } else {
943792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayPos = mRand.nextInt(mPlayListLen);
944792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
945792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mHistory.clear();
946792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
947792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            saveBookmarkIfNeeded();
948e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            openCurrentAndNext();
949d8c696753687ddb0177f869fad5f709eadfe27afJeffrey Sharkey            if (oldId != getAudioId()) {
950792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                notifyChange(META_CHANGED);
951792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
952792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
953792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
954792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
955792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
956792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Moves the item at index1 to index2.
957792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param index1
958792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param index2
959792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
960792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void moveQueueItem(int index1, int index2) {
961792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
962792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (index1 >= mPlayListLen) {
963792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                index1 = mPlayListLen - 1;
964792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
965792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (index2 >= mPlayListLen) {
966792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                index2 = mPlayListLen - 1;
967792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
968792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (index1 < index2) {
969bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen                long tmp = mPlayList[index1];
970792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                for (int i = index1; i < index2; i++) {
971792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mPlayList[i] = mPlayList[i+1];
972792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
973792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayList[index2] = tmp;
974792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (mPlayPos == index1) {
975792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mPlayPos = index2;
976792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } else if (mPlayPos >= index1 && mPlayPos <= index2) {
977792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        mPlayPos--;
978792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
979792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } else if (index2 < index1) {
980bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen                long tmp = mPlayList[index1];
981792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                for (int i = index1; i > index2; i--) {
982792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mPlayList[i] = mPlayList[i-1];
983792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
984792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayList[index2] = tmp;
985792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (mPlayPos == index1) {
986792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mPlayPos = index2;
987792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } else if (mPlayPos >= index2 && mPlayPos <= index1) {
988792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        mPlayPos++;
989792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
990792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
991792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            notifyChange(QUEUE_CHANGED);
992792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
993792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
994792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
995792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
996792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Returns the current play list
997792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @return An array of integers containing the IDs of the tracks in the play list
998792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
999bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen    public long [] getQueue() {
1000792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1001792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            int len = mPlayListLen;
1002bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen            long [] list = new long[len];
1003792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            for (int i = 0; i < len; i++) {
1004792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                list[i] = mPlayList[i];
1005792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1006792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return list;
1007792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1008792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1009792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1010e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen    private Cursor getCursorForId(long lid) {
1011e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        String id = String.valueOf(lid);
1012e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen
1013e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        Cursor c = getContentResolver().query(
1014e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
1015e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                mCursorCols, "_id=" + id , null, null);
1016c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen        if (c != null) {
1017c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen            c.moveToFirst();
1018c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen        }
1019e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        return c;
1020e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen    }
1021e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen
1022e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen    private void openCurrentAndNext() {
1023792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1024792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mCursor != null) {
1025792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mCursor.close();
1026792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mCursor = null;
1027792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
10288d08ec235831d71fdd7f7b6f7757c2bc19528faeMarco Nelissen
1029792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mPlayListLen == 0) {
1030792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return;
1031792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1032792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            stop(false);
1033792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
103490d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            mCursor = getCursorForId(mPlayList[mPlayPos]);
1035c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen            while(true) {
1036c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                if (mCursor != null && mCursor.getCount() != 0 &&
1037c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                        open(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "/" +
1038c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                                mCursor.getLong(IDCOLIDX))) {
1039c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                    break;
1040c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                }
1041c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                // if we get here then opening the file failed. We can close the cursor now, because
1042c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                // we're either going to create a new one next, or stop trying
1043c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                if (mCursor != null) {
1044c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                    mCursor.close();
1045c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                    mCursor = null;
10466fb8551aad7f7af3f099dfa286df587a31484000Marco Nelissen                }
104790d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                if (mOpenFailedCounter++ < 10 &&  mPlayListLen > 1) {
104890d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    int pos = getNextPosition(false);
104990d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    if (pos < 0) {
105090d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                        gotoIdleState();
105190d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                        if (mIsSupposedToBePlaying) {
105290d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                            mIsSupposedToBePlaying = false;
105390d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                            notifyChange(PLAYSTATE_CHANGED);
105490d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                        }
105590d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                        return;
105690d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    }
105790d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    mPlayPos = pos;
105890d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    stop(false);
105990d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    mPlayPos = pos;
106090d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    mCursor = getCursorForId(mPlayList[mPlayPos]);
106190d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                } else {
106290d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    mOpenFailedCounter = 0;
106390d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    if (!mQuietMode) {
106490d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                        Toast.makeText(this, R.string.playback_failed, Toast.LENGTH_SHORT).show();
106590d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    }
106690d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    Log.d(LOGTAG, "Failed to open file for playback");
1067c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                    gotoIdleState();
1068c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                    if (mIsSupposedToBePlaying) {
1069c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                        mIsSupposedToBePlaying = false;
1070c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                        notifyChange(PLAYSTATE_CHANGED);
1071c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                    }
107290d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    return;
1073e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                }
1074792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
107590d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen
107690d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            // go to bookmark if needed
107790d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            if (isPodcast()) {
107890d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                long bookmark = getBookmark();
107990d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                // Start playing a little bit before the bookmark,
108090d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                // so it's easier to get back in to the narrative.
108190d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                seek(bookmark - 5000);
108290d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            }
108390d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            setNextTrack();
1084792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1085792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1086792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1087e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen    private void setNextTrack() {
1088e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        mNextPlayPos = getNextPosition(false);
1089e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        if (mNextPlayPos >= 0) {
1090e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            long id = mPlayList[mNextPlayPos];
1091e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mPlayer.setNextDataSource(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "/" + id);
10920a7f03fa179055114f63bf4e5b1535aefc65066fJun Tian        } else {
10930a7f03fa179055114f63bf4e5b1535aefc65066fJun Tian            mPlayer.setNextDataSource(null);
1094e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        }
1095e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen    }
1096e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen
1097792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1098792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Opens the specified file and readies it for playback.
1099792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     *
1100792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param path The full path of the file to be opened.
1101792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
110290d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen    public boolean open(String path) {
1103792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1104792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (path == null) {
110590d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                return false;
1106792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1107792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1108792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // if mCursor is null, try to associate path with a database cursor
1109792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mCursor == null) {
1110792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1111792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                ContentResolver resolver = getContentResolver();
1112792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                Uri uri;
1113792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                String where;
1114792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                String selectionArgs[];
1115792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (path.startsWith("content://media/")) {
1116792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    uri = Uri.parse(path);
1117792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    where = null;
1118792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    selectionArgs = null;
1119792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } else {
1120792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                   uri = MediaStore.Audio.Media.getContentUriForPath(path);
1121792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                   where = MediaStore.Audio.Media.DATA + "=?";
1122792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                   selectionArgs = new String[] { path };
1123792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1124792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1125792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                try {
1126792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mCursor = resolver.query(uri, mCursorCols, where, selectionArgs, null);
1127792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    if  (mCursor != null) {
1128792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        if (mCursor.getCount() == 0) {
1129792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                            mCursor.close();
1130792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                            mCursor = null;
1131792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        } else {
1132792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                            mCursor.moveToNext();
1133792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                            ensurePlayListCapacity(1);
1134792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                            mPlayListLen = 1;
1135bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen                            mPlayList[0] = mCursor.getLong(IDCOLIDX);
1136792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                            mPlayPos = 0;
1137792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        }
1138792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    }
1139792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } catch (UnsupportedOperationException ex) {
1140792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1141792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1142792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mFileToPlay = path;
1143792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mPlayer.setDataSource(mFileToPlay);
114490d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            if (mPlayer.isInitialized()) {
1145792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mOpenFailedCounter = 0;
114690d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                return true;
1147792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
114890d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            stop(true);
114990d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            return false;
1150792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1151792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1152792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1153792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1154792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Starts playback of a previously opened file.
1155792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1156792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void play() {
1157085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi        mAudioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_MUSIC,
1158085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi                AudioManager.AUDIOFOCUS_GAIN);
11593d22fc22dd66be08df07c86edd533dfe3d06efc1Jean-Michel Trivi        mAudioManager.registerMediaButtonEventReceiver(new ComponentName(this.getPackageName(),
11603d22fc22dd66be08df07c86edd533dfe3d06efc1Jean-Michel Trivi                MediaButtonIntentReceiver.class.getName()));
1161085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi
1162792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mPlayer.isInitialized()) {
1163272eb78d8836f969f7480454167bdffc6a6f6dd7Thomas Tuttle            // if we are at the end of the song, go to the next song first
11642f9a1ce2f3b0ae72d698c240ce8448bc7cfe3a77Marco Nelissen            long duration = mPlayer.duration();
11652f9a1ce2f3b0ae72d698c240ce8448bc7cfe3a77Marco Nelissen            if (mRepeatMode != REPEAT_CURRENT && duration > 2000 &&
11662f9a1ce2f3b0ae72d698c240ce8448bc7cfe3a77Marco Nelissen                mPlayer.position() >= duration - 2000) {
1167bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                gotoNext(true);
1168272eb78d8836f969f7480454167bdffc6a6f6dd7Thomas Tuttle            }
1169272eb78d8836f969f7480454167bdffc6a6f6dd7Thomas Tuttle
1170792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mPlayer.start();
11717181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen            // make sure we fade in, in case a previous fadein was stopped because
11727181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen            // of another focus loss
11737bae28fb4233e3d028b0436fb28f783dea14927eMarco Nelissen            mMediaplayerHandler.removeMessages(FADEDOWN);
11747181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen            mMediaplayerHandler.sendEmptyMessage(FADEUP);
1175792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1176e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            updateNotification();
1177c1333370c94dd708f2d1555d8aee6ddf2bc88a37Marco Nelissen            if (!mIsSupposedToBePlaying) {
1178347fe57e4c8a860dfbad6f0ec1991171bfa69d77Mike Cleron                mIsSupposedToBePlaying = true;
1179792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                notifyChange(PLAYSTATE_CHANGED);
1180792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1181347fe57e4c8a860dfbad6f0ec1991171bfa69d77Mike Cleron
1182792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        } else if (mPlayListLen <= 0) {
1183792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // This is mostly so that if you press 'play' on a bluetooth headset
1184792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // without every having played anything before, it will still play
1185792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // something.
1186792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            setShuffleMode(SHUFFLE_AUTO);
1187792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1188792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1189e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen
1190e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen    private void updateNotification() {
1191e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        RemoteViews views = new RemoteViews(getPackageName(), R.layout.statusbar);
1192e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        views.setImageViewResource(R.id.icon, R.drawable.stat_notify_musicplayer);
1193e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        if (getAudioId() < 0) {
1194e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            // streaming
1195e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            views.setTextViewText(R.id.trackname, getPath());
1196e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            views.setTextViewText(R.id.artistalbum, null);
1197e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        } else {
1198e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            String artist = getArtistName();
1199e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            views.setTextViewText(R.id.trackname, getTrackName());
1200e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            if (artist == null || artist.equals(MediaStore.UNKNOWN_STRING)) {
1201e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                artist = getString(R.string.unknown_artist_name);
1202e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            }
1203e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            String album = getAlbumName();
1204e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            if (album == null || album.equals(MediaStore.UNKNOWN_STRING)) {
1205e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                album = getString(R.string.unknown_album_name);
1206e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            }
1207e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen
1208e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            views.setTextViewText(R.id.artistalbum,
1209e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    getString(R.string.notification_artist_album, artist, album)
1210e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    );
1211e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        }
1212e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        Notification status = new Notification();
1213e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        status.contentView = views;
1214e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        status.flags |= Notification.FLAG_ONGOING_EVENT;
1215e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        status.icon = R.drawable.stat_notify_musicplayer;
1216e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        status.contentIntent = PendingIntent.getActivity(this, 0,
1217e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                new Intent("com.android.music.PLAYBACK_VIEWER")
1218e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0);
1219e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        startForeground(PLAYBACKSERVICE_STATUS, status);
1220e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen    }
1221e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen
1222792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private void stop(boolean remove_status_icon) {
1223b1c285c7e2da5492b4224f3e66ea5696b6df5fa8Glenn Kasten        if (mPlayer != null && mPlayer.isInitialized()) {
1224792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mPlayer.stop();
1225792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1226792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mFileToPlay = null;
1227792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mCursor != null) {
1228792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mCursor.close();
1229792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mCursor = null;
1230792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1231792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (remove_status_icon) {
1232792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            gotoIdleState();
1233d5fc5b6cc39bc2c2693428fe957bb4ae7f90c0f7Dianne Hackborn        } else {
1234d5fc5b6cc39bc2c2693428fe957bb4ae7f90c0f7Dianne Hackborn            stopForeground(false);
1235792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1236792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (remove_status_icon) {
1237c1333370c94dd708f2d1555d8aee6ddf2bc88a37Marco Nelissen            mIsSupposedToBePlaying = false;
1238792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1239792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1240792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1241792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1242792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Stops playback.
1243792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1244792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void stop() {
1245792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        stop(true);
1246792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1247792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1248792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1249792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Pauses playback (call play() to resume)
1250792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1251792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void pause() {
1252407cf91b661ceeb3ced16dc20af8243b9a0eb632Marco Nelissen        synchronized(this) {
12537181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen            mMediaplayerHandler.removeMessages(FADEUP);
1254407cf91b661ceeb3ced16dc20af8243b9a0eb632Marco Nelissen            if (isPlaying()) {
1255407cf91b661ceeb3ced16dc20af8243b9a0eb632Marco Nelissen                mPlayer.pause();
1256407cf91b661ceeb3ced16dc20af8243b9a0eb632Marco Nelissen                gotoIdleState();
1257407cf91b661ceeb3ced16dc20af8243b9a0eb632Marco Nelissen                mIsSupposedToBePlaying = false;
1258407cf91b661ceeb3ced16dc20af8243b9a0eb632Marco Nelissen                notifyChange(PLAYSTATE_CHANGED);
1259407cf91b661ceeb3ced16dc20af8243b9a0eb632Marco Nelissen                saveBookmarkIfNeeded();
1260407cf91b661ceeb3ced16dc20af8243b9a0eb632Marco Nelissen            }
1261792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1262792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1263792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1264b6e7bf7ddcc861e81219f9869b5dddc101341458Marco Nelissen    /** Returns whether something is currently playing
1265792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     *
1266b6e7bf7ddcc861e81219f9869b5dddc101341458Marco Nelissen     * @return true if something is playing (or will be playing shortly, in case
1267b6e7bf7ddcc861e81219f9869b5dddc101341458Marco Nelissen     * we're currently transitioning between tracks), false if not.
1268792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1269792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public boolean isPlaying() {
1270c1333370c94dd708f2d1555d8aee6ddf2bc88a37Marco Nelissen        return mIsSupposedToBePlaying;
1271792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1272792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1273792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /*
1274792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project      Desired behavior for prev/next/shuffle:
1275792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1276792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project      - NEXT will move to the next track in the list when not shuffling, and to
1277792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        a track randomly picked from the not-yet-played tracks when shuffling.
1278792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        If all tracks have already been played, pick from the full set, but
1279792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        avoid picking the previously played track if possible.
1280792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project      - when shuffling, PREV will go to the previously played track. Hitting PREV
1281792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        again will go to the track played before that, etc. When the start of the
1282792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        history has been reached, PREV is a no-op.
1283792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        When not shuffling, PREV will go to the sequentially previous track (the
1284792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        difference with the shuffle-case is mainly that when not shuffling, the
1285792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        user can back up to tracks that are not in the history).
1286792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1287792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        Example:
1288792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        When playing an album with 10 tracks from the start, and enabling shuffle
1289792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        while playing track 5, the remaining tracks (6-10) will be shuffled, e.g.
1290792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        the final play order might be 1-2-3-4-5-8-10-6-9-7.
1291792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        When hitting 'prev' 8 times while playing track 7 in this example, the
1292792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        user will go to tracks 9-6-10-8-5-4-3-2. If the user then hits 'next',
1293792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        a random track will be picked again. If at any time user disables shuffling
1294792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        the next/previous track will be picked in sequential order again.
1295792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1296792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1297792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void prev() {
1298792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1299792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mShuffleMode == SHUFFLE_NORMAL) {
1300792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                // go to previously-played track and remove it from the history
1301792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                int histsize = mHistory.size();
1302792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (histsize == 0) {
1303792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    // prev is a no-op
1304792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    return;
1305792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1306792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                Integer pos = mHistory.remove(histsize - 1);
1307792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayPos = pos.intValue();
1308792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } else {
1309792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (mPlayPos > 0) {
1310792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mPlayPos--;
1311792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } else {
1312792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mPlayPos = mPlayListLen - 1;
1313792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1314792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1315792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            saveBookmarkIfNeeded();
1316792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            stop(false);
1317e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            openCurrentAndNext();
1318792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            play();
1319792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            notifyChange(META_CHANGED);
1320792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1321792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1322792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1323bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen    /**
1324bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen     * Get the next position to play. Note that this may actually modify mPlayPos
1325bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen     * if playback is in SHUFFLE_AUTO mode and the shuffle list window needed to
1326bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen     * be adjusted. Either way, the return value is the next value that should be
1327bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen     * assigned to mPlayPos;
1328bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen     */
1329bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen    private int getNextPosition(boolean force) {
1330e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        if (mRepeatMode == REPEAT_CURRENT) {
1331e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            if (mPlayPos < 0) return 0;
1332e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            return mPlayPos;
1333e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        } else if (mShuffleMode == SHUFFLE_NORMAL) {
1334bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            // Pick random next track from the not-yet-played ones
1335bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            // TODO: make it work right after adding/removing items in the queue.
1336663fea34b586fbdf46acebdb86e163aca0e50323Marco Nelissen
1337bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            // Store the current file in the history, but keep the history at a
1338bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            // reasonable size
1339bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            if (mPlayPos >= 0) {
1340bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                mHistory.add(mPlayPos);
1341bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            }
1342bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            if (mHistory.size() > MAX_HISTORY_SIZE) {
1343bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                mHistory.removeElementAt(0);
1344bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            }
1345792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1346bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            int numTracks = mPlayListLen;
1347bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            int[] tracks = new int[numTracks];
1348bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            for (int i=0;i < numTracks; i++) {
1349bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                tracks[i] = i;
1350bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            }
13513f502de8e8963224054a97730d132f82ac87d522Marco Nelissen
1352bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            int numHistory = mHistory.size();
1353bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            int numUnplayed = numTracks;
1354bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            for (int i=0;i < numHistory; i++) {
1355bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                int idx = mHistory.get(i).intValue();
1356bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                if (idx < numTracks && tracks[idx] >= 0) {
1357bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    numUnplayed--;
1358bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    tracks[idx] = -1;
1359792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1360bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            }
1361792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1362bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            // 'numUnplayed' now indicates how many tracks have not yet
1363bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            // been played, and 'tracks' contains the indices of those
1364bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            // tracks.
1365bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            if (numUnplayed <=0) {
1366bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                // everything's already been played
1367bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                if (mRepeatMode == REPEAT_ALL || force) {
1368bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    //pick from full set
1369bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    numUnplayed = numTracks;
1370bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    for (int i=0;i < numTracks; i++) {
1371bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                        tracks[i] = i;
1372792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    }
1373bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                } else {
1374bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    // all done
1375bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    return -1;
1376792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1377bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            }
1378bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            int skip = mRand.nextInt(numUnplayed);
1379bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            int cnt = -1;
1380bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            while (true) {
1381bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                while (tracks[++cnt] < 0)
1382bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    ;
1383bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                skip--;
1384bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                if (skip < 0) {
1385bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    break;
1386792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1387bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            }
1388bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            return cnt;
1389bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen        } else if (mShuffleMode == SHUFFLE_AUTO) {
1390bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            doAutoShuffleUpdate();
1391bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            return mPlayPos + 1;
1392bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen        } else {
1393bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            if (mPlayPos >= mPlayListLen - 1) {
1394bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                // we're at the end of the list
1395bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                if (mRepeatMode == REPEAT_NONE && !force) {
1396bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    // all done
1397bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    return -1;
1398bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                } else if (mRepeatMode == REPEAT_ALL || force) {
1399bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    return 0;
1400792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1401bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                return -1;
1402792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } else {
1403bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                return mPlayPos + 1;
1404bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            }
1405bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen        }
1406bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen    }
1407bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen
1408bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen    public void gotoNext(boolean force) {
1409bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen        synchronized (this) {
1410bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            if (mPlayListLen <= 0) {
1411bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                Log.d(LOGTAG, "No play queue");
1412bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                return;
1413bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            }
1414bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen
1415bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            int pos = getNextPosition(force);
1416bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            if (pos < 0) {
1417bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                gotoIdleState();
1418bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                if (mIsSupposedToBePlaying) {
1419bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    mIsSupposedToBePlaying = false;
1420bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    notifyChange(PLAYSTATE_CHANGED);
1421792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1422bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                return;
1423792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1424bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            mPlayPos = pos;
1425792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            saveBookmarkIfNeeded();
1426792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            stop(false);
1427e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mPlayPos = pos;
1428e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            openCurrentAndNext();
1429792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            play();
1430792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            notifyChange(META_CHANGED);
1431792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1432792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1433792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1434792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private void gotoIdleState() {
1435792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mDelayedStopHandler.removeCallbacksAndMessages(null);
1436792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        Message msg = mDelayedStopHandler.obtainMessage();
1437792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY);
1438d5fc5b6cc39bc2c2693428fe957bb4ae7f90c0f7Dianne Hackborn        stopForeground(true);
1439792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1440792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1441792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private void saveBookmarkIfNeeded() {
1442792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        try {
1443792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (isPodcast()) {
1444792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                long pos = position();
1445792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                long bookmark = getBookmark();
1446792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                long duration = duration();
1447792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if ((pos < bookmark && (pos + 10000) > bookmark) ||
1448792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        (pos > bookmark && (pos - 10000) < bookmark)) {
1449792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    // The existing bookmark is close to the current
1450792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    // position, so don't update it.
1451792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    return;
1452792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1453792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (pos < 15000 || (pos + 10000) > duration) {
1454792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    // if we're near the start or end, clear the bookmark
1455792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    pos = 0;
1456792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1457792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1458792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                // write 'pos' to the bookmark field
1459792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                ContentValues values = new ContentValues();
1460792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                values.put(MediaStore.Audio.Media.BOOKMARK, pos);
1461792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                Uri uri = ContentUris.withAppendedId(
1462792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, mCursor.getLong(IDCOLIDX));
1463792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                getContentResolver().update(uri, values, null, null);
1464792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1465792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        } catch (SQLiteException ex) {
1466792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1467792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1468792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1469792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    // Make sure there are at least 5 items after the currently playing item
1470792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    // and no more than 10 items before.
1471792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private void doAutoShuffleUpdate() {
1472792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        boolean notify = false;
14733f502de8e8963224054a97730d132f82ac87d522Marco Nelissen
1474792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // remove old entries
1475792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mPlayPos > 10) {
1476792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            removeTracks(0, mPlayPos - 9);
1477792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            notify = true;
1478792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1479792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // add new entries if needed
1480792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        int to_add = 7 - (mPlayListLen - (mPlayPos < 0 ? -1 : mPlayPos));
1481792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        for (int i = 0; i < to_add; i++) {
1482792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // pick something at random from the list
14833f502de8e8963224054a97730d132f82ac87d522Marco Nelissen
14843f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            int lookback = mHistory.size();
14853f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            int idx = -1;
14863f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            while(true) {
14873f502de8e8963224054a97730d132f82ac87d522Marco Nelissen                idx = mRand.nextInt(mAutoShuffleList.length);
14883f502de8e8963224054a97730d132f82ac87d522Marco Nelissen                if (!wasRecentlyUsed(idx, lookback)) {
14893f502de8e8963224054a97730d132f82ac87d522Marco Nelissen                    break;
14903f502de8e8963224054a97730d132f82ac87d522Marco Nelissen                }
14913f502de8e8963224054a97730d132f82ac87d522Marco Nelissen                lookback /= 2;
14923f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            }
14933f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            mHistory.add(idx);
14943f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            if (mHistory.size() > MAX_HISTORY_SIZE) {
14953f502de8e8963224054a97730d132f82ac87d522Marco Nelissen                mHistory.remove(0);
14963f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            }
1497792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            ensurePlayListCapacity(mPlayListLen + 1);
14983f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            mPlayList[mPlayListLen++] = mAutoShuffleList[idx];
1499792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            notify = true;
1500792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1501792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (notify) {
1502792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            notifyChange(QUEUE_CHANGED);
1503792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1504792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1505792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
15063f502de8e8963224054a97730d132f82ac87d522Marco Nelissen    // check that the specified idx is not in the history (but only look at at
15073f502de8e8963224054a97730d132f82ac87d522Marco Nelissen    // most lookbacksize entries in the history)
15083f502de8e8963224054a97730d132f82ac87d522Marco Nelissen    private boolean wasRecentlyUsed(int idx, int lookbacksize) {
15093f502de8e8963224054a97730d132f82ac87d522Marco Nelissen
15103f502de8e8963224054a97730d132f82ac87d522Marco Nelissen        // early exit to prevent infinite loops in case idx == mPlayPos
15113f502de8e8963224054a97730d132f82ac87d522Marco Nelissen        if (lookbacksize == 0) {
15123f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            return false;
15133f502de8e8963224054a97730d132f82ac87d522Marco Nelissen        }
15143f502de8e8963224054a97730d132f82ac87d522Marco Nelissen
15153f502de8e8963224054a97730d132f82ac87d522Marco Nelissen        int histsize = mHistory.size();
15163f502de8e8963224054a97730d132f82ac87d522Marco Nelissen        if (histsize < lookbacksize) {
15173f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            Log.d(LOGTAG, "lookback too big");
15183f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            lookbacksize = histsize;
15193f502de8e8963224054a97730d132f82ac87d522Marco Nelissen        }
15203f502de8e8963224054a97730d132f82ac87d522Marco Nelissen        int maxidx = histsize - 1;
15213f502de8e8963224054a97730d132f82ac87d522Marco Nelissen        for (int i = 0; i < lookbacksize; i++) {
15223f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            long entry = mHistory.get(maxidx - i);
15233f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            if (entry == idx) {
15243f502de8e8963224054a97730d132f82ac87d522Marco Nelissen                return true;
15253f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            }
15263f502de8e8963224054a97730d132f82ac87d522Marco Nelissen        }
15273f502de8e8963224054a97730d132f82ac87d522Marco Nelissen        return false;
15283f502de8e8963224054a97730d132f82ac87d522Marco Nelissen    }
15293f502de8e8963224054a97730d132f82ac87d522Marco Nelissen
1530792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    // A simple variation of Random that makes sure that the
1531792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    // value it returns is not equal to the value it returned
1532792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    // previously, unless the interval is 1.
1533756c3f57f11a7bb3bf1cf1333ee8ffbc51039748Marco Nelissen    private static class Shuffler {
1534792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        private int mPrevious;
1535792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        private Random mRandom = new Random();
1536792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public int nextInt(int interval) {
1537792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            int ret;
1538792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            do {
1539792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                ret = mRandom.nextInt(interval);
1540792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } while (ret == mPrevious && interval > 1);
1541792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mPrevious = ret;
1542792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return ret;
1543792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1544792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    };
1545792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1546792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private boolean makeAutoShuffleList() {
1547792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        ContentResolver res = getContentResolver();
1548792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        Cursor c = null;
1549792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        try {
1550792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            c = res.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
1551792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    new String[] {MediaStore.Audio.Media._ID}, MediaStore.Audio.Media.IS_MUSIC + "=1",
1552792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    null, null);
1553792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (c == null || c.getCount() == 0) {
1554792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return false;
1555792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1556792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            int len = c.getCount();
1557bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen            long [] list = new long[len];
1558792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            for (int i = 0; i < len; i++) {
1559792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                c.moveToNext();
1560bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen                list[i] = c.getLong(0);
1561792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1562792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mAutoShuffleList = list;
1563792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return true;
1564792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        } catch (RuntimeException ex) {
1565792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        } finally {
1566792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (c != null) {
1567792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                c.close();
1568792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1569792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1570792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return false;
1571792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1572792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1573792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1574792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Removes the range of tracks specified from the play list. If a file within the range is
1575792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * the file currently being played, playback will move to the next file after the
1576792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * range.
1577792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param first The first file to be removed
1578792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param last The last file to be removed
1579792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @return the number of tracks deleted
1580792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1581792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public int removeTracks(int first, int last) {
1582792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        int numremoved = removeTracksInternal(first, last);
1583792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (numremoved > 0) {
1584792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            notifyChange(QUEUE_CHANGED);
1585792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1586792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return numremoved;
1587792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1588792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1589792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private int removeTracksInternal(int first, int last) {
1590792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1591792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (last < first) return 0;
1592792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (first < 0) first = 0;
1593792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (last >= mPlayListLen) last = mPlayListLen - 1;
1594792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1595792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            boolean gotonext = false;
1596792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (first <= mPlayPos && mPlayPos <= last) {
1597792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayPos = first;
1598792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                gotonext = true;
1599792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } else if (mPlayPos > last) {
1600792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayPos -= (last - first + 1);
1601792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1602792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            int num = mPlayListLen - last - 1;
1603792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            for (int i = 0; i < num; i++) {
1604792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayList[first + i] = mPlayList[last + 1 + i];
1605792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1606792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mPlayListLen -= last - first + 1;
1607792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1608792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (gotonext) {
1609792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (mPlayListLen == 0) {
1610792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    stop(true);
1611792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mPlayPos = -1;
16123aa9ad029bc85da2e07bba019cec928ad5d74043Marco Nelissen                    if (mCursor != null) {
16133aa9ad029bc85da2e07bba019cec928ad5d74043Marco Nelissen                        mCursor.close();
16143aa9ad029bc85da2e07bba019cec928ad5d74043Marco Nelissen                        mCursor = null;
16153aa9ad029bc85da2e07bba019cec928ad5d74043Marco Nelissen                    }
1616792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } else {
1617792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    if (mPlayPos >= mPlayListLen) {
1618792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        mPlayPos = 0;
1619792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    }
1620792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    boolean wasPlaying = isPlaying();
1621792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    stop(false);
1622e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    openCurrentAndNext();
1623792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    if (wasPlaying) {
1624792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        play();
1625792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    }
1626792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
16273aa9ad029bc85da2e07bba019cec928ad5d74043Marco Nelissen                notifyChange(META_CHANGED);
1628792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1629792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return last - first + 1;
1630792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1631792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1632792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1633792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1634792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Removes all instances of the track with the given id
1635792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * from the playlist.
1636792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param id The id to be removed
1637792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @return how many instances of the track were removed
1638792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1639bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen    public int removeTrack(long id) {
1640792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        int numremoved = 0;
1641792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1642792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            for (int i = 0; i < mPlayListLen; i++) {
1643792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (mPlayList[i] == id) {
1644792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    numremoved += removeTracksInternal(i, i);
1645792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    i--;
1646792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1647792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1648792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1649792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (numremoved > 0) {
1650792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            notifyChange(QUEUE_CHANGED);
1651792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1652792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return numremoved;
1653792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1654792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1655792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void setShuffleMode(int shufflemode) {
1656792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized(this) {
1657792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mShuffleMode == shufflemode && mPlayListLen > 0) {
1658792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return;
1659792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1660792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mShuffleMode = shufflemode;
1661792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mShuffleMode == SHUFFLE_AUTO) {
1662792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (makeAutoShuffleList()) {
1663792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mPlayListLen = 0;
1664792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    doAutoShuffleUpdate();
1665792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mPlayPos = 0;
1666e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    openCurrentAndNext();
1667792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    play();
1668792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    notifyChange(META_CHANGED);
1669792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    return;
1670792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } else {
1671792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    // failed to build a list of files to shuffle
1672792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mShuffleMode = SHUFFLE_NONE;
1673792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1674792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1675792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            saveQueue(false);
1676792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1677792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1678792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public int getShuffleMode() {
1679792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return mShuffleMode;
1680792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1681792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1682792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void setRepeatMode(int repeatmode) {
1683792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized(this) {
1684792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mRepeatMode = repeatmode;
1685e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            setNextTrack();
1686792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            saveQueue(false);
1687792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1688792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1689792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public int getRepeatMode() {
1690792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return mRepeatMode;
1691792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1692792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1693792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public int getMediaMountedCount() {
1694792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return mMediaMountedCount;
1695792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1696792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1697792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1698792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Returns the path of the currently playing file, or null if
1699792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * no file is currently playing.
1700792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1701792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public String getPath() {
1702792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return mFileToPlay;
1703792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1704792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1705792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1706792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Returns the rowid of the currently playing file, or -1 if
1707792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * no file is currently playing.
1708792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1709bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen    public long getAudioId() {
1710792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1711792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mPlayPos >= 0 && mPlayer.isInitialized()) {
1712792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return mPlayList[mPlayPos];
1713792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1714792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1715792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return -1;
1716792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1717792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1718792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1719792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Returns the position in the queue
1720792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @return the position in the queue
1721792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1722792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public int getQueuePosition() {
1723792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized(this) {
1724792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return mPlayPos;
1725792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1726792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1727792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1728792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1729792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Starts playing the track at the given position in the queue.
1730792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param pos The position in the queue of the track that will be played.
1731792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1732792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void setQueuePosition(int pos) {
1733792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized(this) {
1734792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            stop(false);
1735792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mPlayPos = pos;
1736e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            openCurrentAndNext();
1737792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            play();
1738792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            notifyChange(META_CHANGED);
1739ec0c57a414c3563ebbf5767d92787a6a3f4a8820Marco Nelissen            if (mShuffleMode == SHUFFLE_AUTO) {
1740ec0c57a414c3563ebbf5767d92787a6a3f4a8820Marco Nelissen                doAutoShuffleUpdate();
1741ec0c57a414c3563ebbf5767d92787a6a3f4a8820Marco Nelissen            }
1742792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1743792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1744792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1745792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public String getArtistName() {
1746792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized(this) {
1747792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mCursor == null) {
1748792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return null;
1749792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1750792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return mCursor.getString(mCursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST));
1751792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1752792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1753792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1754bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen    public long getArtistId() {
1755792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1756792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mCursor == null) {
1757792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return -1;
1758792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1759bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen            return mCursor.getLong(mCursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST_ID));
1760792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1761792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1762792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1763792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public String getAlbumName() {
1764792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1765792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mCursor == null) {
1766792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return null;
1767792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1768792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return mCursor.getString(mCursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM));
1769792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1770792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1771792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1772bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen    public long getAlbumId() {
1773792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1774792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mCursor == null) {
1775792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return -1;
1776792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1777bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen            return mCursor.getLong(mCursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID));
1778792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1779792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1780792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1781792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public String getTrackName() {
1782792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1783792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mCursor == null) {
1784792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return null;
1785792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1786792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return mCursor.getString(mCursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));
1787792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1788792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1789792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1790792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private boolean isPodcast() {
1791792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1792792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mCursor == null) {
1793792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return false;
1794792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1795792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return (mCursor.getInt(PODCASTCOLIDX) > 0);
1796792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1797792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1798792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1799792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private long getBookmark() {
1800792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1801792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mCursor == null) {
1802792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return 0;
1803792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1804792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return mCursor.getLong(BOOKMARKCOLIDX);
1805792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1806792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1807792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1808792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1809792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Returns the duration of the file in milliseconds.
1810792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Currently this method returns -1 for the duration of MIDI files.
1811792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1812792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public long duration() {
1813792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mPlayer.isInitialized()) {
1814792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return mPlayer.duration();
1815792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1816792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return -1;
1817792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1818792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1819792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1820792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Returns the current playback position in milliseconds
1821792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1822792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public long position() {
1823792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mPlayer.isInitialized()) {
1824792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return mPlayer.position();
1825792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1826792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return -1;
1827792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1828792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1829792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1830792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Seeks to the position specified.
1831792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     *
1832792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param pos The position to seek to, in milliseconds
1833792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1834792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public long seek(long pos) {
1835792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mPlayer.isInitialized()) {
1836792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (pos < 0) pos = 0;
1837792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (pos > mPlayer.duration()) pos = mPlayer.duration();
1838792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return mPlayer.seek(pos);
1839792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1840792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return -1;
1841792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1842792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1843792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
18441cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent     * Sets the audio session ID.
18451cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent     *
18461cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent     * @param sessionId: the audio session ID.
18471cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent     */
18481cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent    public void setAudioSessionId(int sessionId) {
18491cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent        synchronized (this) {
18501cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent            mPlayer.setAudioSessionId(sessionId);
18511cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent        }
18521cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent    }
18531cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent
18541cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent    /**
18551cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent     * Returns the audio session ID.
18561cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent     */
18571cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent    public int getAudioSessionId() {
18581cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent        synchronized (this) {
18591cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent            return mPlayer.getAudioSessionId();
18601cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent        }
18611cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent    }
18621cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent
18631cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent    /**
1864792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Provides a unified interface for dealing with midi files and
1865792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * other media files.
1866792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1867792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private class MultiPlayer {
1868308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        private CompatMediaPlayer mCurrentMediaPlayer = new CompatMediaPlayer();
1869308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        private CompatMediaPlayer mNextMediaPlayer;
1870792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        private Handler mHandler;
1871792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        private boolean mIsInitialized = false;
1872792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1873792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public MultiPlayer() {
1874e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mCurrentMediaPlayer.setWakeMode(MediaPlaybackService.this, PowerManager.PARTIAL_WAKE_LOCK);
1875792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1876792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1877792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void setDataSource(String path) {
187890d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            mIsInitialized = setDataSourceImpl(mCurrentMediaPlayer, path);
187990d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            if (mIsInitialized) {
188090d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                setNextDataSource(null);
188190d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            }
1882e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        }
1883e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen
188490d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen        private boolean setDataSourceImpl(MediaPlayer player, String path) {
1885792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            try {
1886e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                player.reset();
1887e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                player.setOnPreparedListener(null);
1888792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (path.startsWith("content://")) {
1889e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    player.setDataSource(MediaPlaybackService.this, Uri.parse(path));
1890792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } else {
1891e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    player.setDataSource(path);
1892792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1893e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                player.setAudioStreamType(AudioManager.STREAM_MUSIC);
1894e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                player.prepare();
1895792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } catch (IOException ex) {
1896792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                // TODO: notify the user why the file couldn't be opened
189790d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                return false;
1898792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } catch (IllegalArgumentException ex) {
1899792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                // TODO: notify the user why the file couldn't be opened
190090d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                return false;
1901792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1902e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            player.setOnCompletionListener(listener);
1903e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            player.setOnErrorListener(errorListener);
1904f2ef3b5153fd2f447939f267d8887ff3bba0d13fMarco Nelissen            Intent i = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
1905f2ef3b5153fd2f447939f267d8887ff3bba0d13fMarco Nelissen            i.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, getAudioSessionId());
1906f2ef3b5153fd2f447939f267d8887ff3bba0d13fMarco Nelissen            i.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName());
1907f2ef3b5153fd2f447939f267d8887ff3bba0d13fMarco Nelissen            sendBroadcast(i);
190890d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            return true;
1909792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1910e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen
1911e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        public void setNextDataSource(String path) {
1912e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mCurrentMediaPlayer.setNextMediaPlayer(null);
1913e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            if (mNextMediaPlayer != null) {
1914e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                mNextMediaPlayer.release();
1915e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                mNextMediaPlayer = null;
1916e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            }
1917e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            if (path == null) {
1918e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                return;
1919e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            }
1920308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            mNextMediaPlayer = new CompatMediaPlayer();
1921e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mNextMediaPlayer.setWakeMode(MediaPlaybackService.this, PowerManager.PARTIAL_WAKE_LOCK);
1922e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mNextMediaPlayer.setAudioSessionId(getAudioSessionId());
192390d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            if (setDataSourceImpl(mNextMediaPlayer, path)) {
192490d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                mCurrentMediaPlayer.setNextMediaPlayer(mNextMediaPlayer);
192590d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            } else {
192690d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                // failed to open next, we'll transition the old fashioned way,
192790d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                // which will skip over the faulty file
192890d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                mNextMediaPlayer.release();
192990d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                mNextMediaPlayer = null;
193090d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            }
1931e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        }
1932792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1933792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public boolean isInitialized() {
1934792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return mIsInitialized;
1935792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1936792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1937792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void start() {
19383988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen            MusicUtils.debugLog(new Exception("MultiPlayer.start called"));
1939e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mCurrentMediaPlayer.start();
1940792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1941792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1942792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void stop() {
1943e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mCurrentMediaPlayer.reset();
1944792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mIsInitialized = false;
1945792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1946792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
19472b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        /**
19482b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen         * You CANNOT use this player anymore after calling release()
19492b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen         */
19502b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        public void release() {
19512b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            stop();
1952e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mCurrentMediaPlayer.release();
19532b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        }
19542b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen
1955792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void pause() {
1956e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mCurrentMediaPlayer.pause();
1957792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1958792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1959792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void setHandler(Handler handler) {
1960792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mHandler = handler;
1961792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1962792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1963792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        MediaPlayer.OnCompletionListener listener = new MediaPlayer.OnCompletionListener() {
1964792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            public void onCompletion(MediaPlayer mp) {
1965e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                if (mp == mCurrentMediaPlayer && mNextMediaPlayer != null) {
1966e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    mCurrentMediaPlayer.release();
1967e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    mCurrentMediaPlayer = mNextMediaPlayer;
1968e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    mNextMediaPlayer = null;
1969e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    mHandler.sendEmptyMessage(TRACK_WENT_TO_NEXT);
1970e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                } else {
1971e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    // Acquire a temporary wakelock, since when we return from
1972e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    // this callback the MediaPlayer will release its wakelock
1973e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    // and allow the device to go to sleep.
1974e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    // This temporary wakelock is released when the RELEASE_WAKELOCK
1975e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    // message is processed, but just in case, put a timeout on it.
1976e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    mWakeLock.acquire(30000);
1977e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    mHandler.sendEmptyMessage(TRACK_ENDED);
1978e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    mHandler.sendEmptyMessage(RELEASE_WAKELOCK);
1979e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                }
1980792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1981792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        };
1982792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1983792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        MediaPlayer.OnErrorListener errorListener = new MediaPlayer.OnErrorListener() {
1984792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            public boolean onError(MediaPlayer mp, int what, int extra) {
1985792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                switch (what) {
1986792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
1987792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mIsInitialized = false;
1988e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    mCurrentMediaPlayer.release();
1989792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    // Creating a new MediaPlayer and settings its wakemode does not
1990792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    // require the media service, so it's OK to do this now, while the
1991792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    // service is still being restarted
1992308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                    mCurrentMediaPlayer = new CompatMediaPlayer();
1993e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    mCurrentMediaPlayer.setWakeMode(MediaPlaybackService.this, PowerManager.PARTIAL_WAKE_LOCK);
1994792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mHandler.sendMessageDelayed(mHandler.obtainMessage(SERVER_DIED), 2000);
1995792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    return true;
1996792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                default:
1997e99341f9674462607f3fd5e8dbd100b34b973e1bMarco Nelissen                    Log.d("MultiPlayer", "Error: " + what + "," + extra);
1998792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    break;
1999792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
2000792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return false;
2001792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project           }
2002792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        };
2003792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
2004792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public long duration() {
2005e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            return mCurrentMediaPlayer.getDuration();
2006792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2007792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
2008792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public long position() {
2009e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            return mCurrentMediaPlayer.getCurrentPosition();
2010792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2011792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
2012792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public long seek(long whereto) {
2013e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mCurrentMediaPlayer.seekTo((int) whereto);
2014792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return whereto;
2015792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2016792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
2017792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void setVolume(float vol) {
2018e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mCurrentMediaPlayer.setVolume(vol, vol);
2019792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
20201cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent
20211cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent        public void setAudioSessionId(int sessionId) {
2022e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mCurrentMediaPlayer.setAudioSessionId(sessionId);
20231cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent        }
20241cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent
20251cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent        public int getAudioSessionId() {
2026e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            return mCurrentMediaPlayer.getAudioSessionId();
20271cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent        }
2028792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
2029792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
2030308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen    static class CompatMediaPlayer extends MediaPlayer implements OnCompletionListener {
2031308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen
2032308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        private boolean mCompatMode = true;
2033308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        private MediaPlayer mNextPlayer;
2034308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        private OnCompletionListener mCompletion;
2035308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen
2036308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        public CompatMediaPlayer() {
2037308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            try {
2038308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                MediaPlayer.class.getMethod("setNextMediaPlayer", MediaPlayer.class);
2039308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                mCompatMode = false;
2040308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            } catch (NoSuchMethodException e) {
2041308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                mCompatMode = true;
2042308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                super.setOnCompletionListener(this);
2043308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            }
2044308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        }
2045308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen
2046308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        public void setNextMediaPlayer(MediaPlayer next) {
2047308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            if (mCompatMode) {
2048308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                mNextPlayer = next;
2049308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            } else {
2050308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                super.setNextMediaPlayer(next);
2051308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            }
2052308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        }
2053308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen
2054308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        @Override
2055308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        public void setOnCompletionListener(OnCompletionListener listener) {
2056308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            if (mCompatMode) {
2057308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                mCompletion = listener;
2058308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            } else {
2059308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                super.setOnCompletionListener(listener);
2060308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            }
2061308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        }
2062308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen
2063308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        @Override
2064308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        public void onCompletion(MediaPlayer mp) {
2065308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            if (mNextPlayer != null) {
2066308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                // as it turns out, starting a new MediaPlayer on the completion
2067308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                // of a previous player ends up slightly overlapping the two
2068308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                // playbacks, so slightly delaying the start of the next player
2069308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                // gives a better user experience
2070308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                SystemClock.sleep(50);
2071308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                mNextPlayer.start();
2072308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            }
2073308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            mCompletion.onCompletion(this);
2074308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        }
2075308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen    }
2076308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen
20772b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen    /*
20782b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen     * By making this a static class with a WeakReference to the Service, we
20792b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen     * ensure that the Service can be GCd even when the system process still
20802b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen     * has a remote reference to the stub.
20812b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen     */
20822b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen    static class ServiceStub extends IMediaPlaybackService.Stub {
20832b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        WeakReference<MediaPlaybackService> mService;
20842b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen
20852b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        ServiceStub(MediaPlaybackService service) {
20862b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService = new WeakReference<MediaPlaybackService>(service);
20872b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        }
20882b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen
20898d08ec235831d71fdd7f7b6f7757c2bc19528faeMarco Nelissen        public void openFile(String path)
2090792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        {
20918d08ec235831d71fdd7f7b6f7757c2bc19528faeMarco Nelissen            mService.get().open(path);
2092792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2093bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen        public void open(long [] list, int position) {
20942b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService.get().open(list, position);
2095792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2096792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public int getQueuePosition() {
20972b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getQueuePosition();
2098792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2099792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void setQueuePosition(int index) {
21002b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService.get().setQueuePosition(index);
2101792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2102792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public boolean isPlaying() {
21032b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().isPlaying();
2104792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2105792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void stop() {
21062b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService.get().stop();
2107792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2108792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void pause() {
21092b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService.get().pause();
2110792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2111792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void play() {
21122b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService.get().play();
2113792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2114792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void prev() {
21152b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService.get().prev();
2116792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2117792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void next() {
2118bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            mService.get().gotoNext(true);
2119792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2120792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public String getTrackName() {
21212b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getTrackName();
2122792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2123792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public String getAlbumName() {
21242b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getAlbumName();
2125792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2126bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen        public long getAlbumId() {
21272b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getAlbumId();
2128792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2129792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public String getArtistName() {
21302b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getArtistName();
2131792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2132bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen        public long getArtistId() {
21332b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getArtistId();
2134792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2135bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen        public void enqueue(long [] list , int action) {
21362b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService.get().enqueue(list, action);
2137792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2138bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen        public long [] getQueue() {
21392b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getQueue();
2140792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2141792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void moveQueueItem(int from, int to) {
21422b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService.get().moveQueueItem(from, to);
2143792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2144792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public String getPath() {
21452b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getPath();
2146792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2147bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen        public long getAudioId() {
21482b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getAudioId();
2149792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2150792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public long position() {
21512b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().position();
2152792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2153792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public long duration() {
21542b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().duration();
2155792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2156792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public long seek(long pos) {
21572b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().seek(pos);
2158792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2159792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void setShuffleMode(int shufflemode) {
21602b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService.get().setShuffleMode(shufflemode);
2161792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2162792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public int getShuffleMode() {
21632b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getShuffleMode();
2164792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2165792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public int removeTracks(int first, int last) {
21662b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().removeTracks(first, last);
2167792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2168bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen        public int removeTrack(long id) {
21692b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().removeTrack(id);
2170792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2171792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void setRepeatMode(int repeatmode) {
21722b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService.get().setRepeatMode(repeatmode);
2173792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2174792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public int getRepeatMode() {
21752b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getRepeatMode();
2176792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2177792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public int getMediaMountedCount() {
21782b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getMediaMountedCount();
2179792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
21801cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent        public int getAudioSessionId() {
21811cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent            return mService.get().getAudioSessionId();
21821cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent        }
21832b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen    }
21843988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen
21853988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen    @Override
21863988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
2187bf555cee7a01aed223b05144d1cec0cffdd9a6aeMarco Nelissen        writer.println("" + mPlayListLen + " items in queue, currently at index " + mPlayPos);
21883988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen        writer.println("Currently loaded:");
21893988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen        writer.println(getArtistName());
21903988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen        writer.println(getAlbumName());
21913988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen        writer.println(getTrackName());
21923988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen        writer.println(getPath());
21933988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen        writer.println("playing: " + mIsSupposedToBePlaying);
2194e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        writer.println("actual: " + mPlayer.mCurrentMediaPlayer.isPlaying());
2195bf555cee7a01aed223b05144d1cec0cffdd9a6aeMarco Nelissen        writer.println("shuffle mode: " + mShuffleMode);
21963988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen        MusicUtils.debugDump(writer);
21973988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen    }
21983988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen
21992b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen    private final IBinder mBinder = new ServiceStub(this);
2200792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project}
2201