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;
76b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
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;
80b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
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
98b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He    public static final String TOGGLEPAUSE_ACTION =
99b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            "com.android.music.musicservicecommand.togglepause";
100792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final String PAUSE_ACTION = "com.android.music.musicservicecommand.pause";
101792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final String PREVIOUS_ACTION = "com.android.music.musicservicecommand.previous";
102792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public static final String NEXT_ACTION = "com.android.music.musicservicecommand.next";
103792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
104792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private static final int TRACK_ENDED = 1;
105792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private static final int RELEASE_WAKELOCK = 2;
106792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private static final int SERVER_DIED = 3;
1077181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen    private static final int FOCUSCHANGE = 4;
1082da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen    private static final int FADEDOWN = 5;
1092da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen    private static final int FADEUP = 6;
110e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen    private static final int TRACK_WENT_TO_NEXT = 7;
1113ec2ad9c8758d300864be808e491ae933dc9cd6bMarco Nelissen    private static final int MAX_HISTORY_SIZE = 100;
112b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
113792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private MultiPlayer mPlayer;
114792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private String mFileToPlay;
115792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private int mShuffleMode = SHUFFLE_NONE;
116792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private int mRepeatMode = REPEAT_NONE;
117792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private int mMediaMountedCount = 0;
118b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He    private long[] mAutoShuffleList = null;
119b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He    private long[] mPlayList = null;
120792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private int mPlayListLen = 0;
121792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private Vector<Integer> mHistory = new Vector<Integer>(MAX_HISTORY_SIZE);
122792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private Cursor mCursor;
123792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private int mPlayPos = -1;
124e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen    private int mNextPlayPos = -1;
125792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private static final String LOGTAG = "MediaPlaybackService";
126792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private final Shuffler mRand = new Shuffler();
127792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private int mOpenFailedCounter = 0;
128792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    String[] mCursorCols = new String[] {
129b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            "audio._id AS _id", // index must match IDCOLIDX below
130b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.ALBUM,
131b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.DATA,
132b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            MediaStore.Audio.Media.MIME_TYPE, MediaStore.Audio.Media.ALBUM_ID,
133792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            MediaStore.Audio.Media.ARTIST_ID,
134792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            MediaStore.Audio.Media.IS_PODCAST, // index must match PODCASTCOLIDX below
135b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            MediaStore.Audio.Media.BOOKMARK // index must match BOOKMARKCOLIDX below
136792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    };
137792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private final static int IDCOLIDX = 0;
138792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private final static int PODCASTCOLIDX = 8;
139792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private final static int BOOKMARKCOLIDX = 9;
140792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private BroadcastReceiver mUnmountReceiver = null;
141792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private WakeLock mWakeLock;
142792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private int mServiceStartId = -1;
143792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private boolean mServiceInUse = false;
144c1333370c94dd708f2d1555d8aee6ddf2bc88a37Marco Nelissen    private boolean mIsSupposedToBePlaying = false;
145792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private boolean mQuietMode = false;
146085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi    private AudioManager mAudioManager;
1478d08ec235831d71fdd7f7b6f7757c2bc19528faeMarco Nelissen    private boolean mQueueIsSaveable = true;
148085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi    // used to track what type of audio focus loss caused the playback to pause
149085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi    private boolean mPausedByTransientLossOfFocus = false;
150085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi
151792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private SharedPreferences mPreferences;
152792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    // We use this to distinguish between different cards when saving/restoring playlists.
153792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    // This will have to change if we want to support multiple simultaneous cards.
154792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private int mCardId;
155b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
156490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project    private MediaAppWidgetProvider mAppWidgetProvider = MediaAppWidgetProvider.getInstance();
157b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
158792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    // interval after which we stop the service when idle
159aa331877dd1a0777042481e5c925cd951682588fJean-Michel Trivi    private static final int IDLE_DELAY = 60000;
1608717d3490e179e9591eac98a91a14995d98291adMarco Nelissen
1618717d3490e179e9591eac98a91a14995d98291adMarco Nelissen    private RemoteControlClient mRemoteControlClient;
1628717d3490e179e9591eac98a91a14995d98291adMarco Nelissen
163792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private Handler mMediaplayerHandler = new Handler() {
164792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        float mCurrentVolume = 1.0f;
165792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        @Override
166792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void handleMessage(Message msg) {
1673988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen            MusicUtils.debugLog("mMediaplayerHandler.handleMessage " + msg.what);
168792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            switch (msg.what) {
1692da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                case FADEDOWN:
1702da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                    mCurrentVolume -= .05f;
1712da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                    if (mCurrentVolume > .2f) {
1722da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                        mMediaplayerHandler.sendEmptyMessageDelayed(FADEDOWN, 10);
173792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    } else {
1742da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                        mCurrentVolume = .2f;
1752da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                    }
1762da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                    mPlayer.setVolume(mCurrentVolume);
1772da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                    break;
1782da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                case FADEUP:
1792da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                    mCurrentVolume += .01f;
1802da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                    if (mCurrentVolume < 1.0f) {
1812da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                        mMediaplayerHandler.sendEmptyMessageDelayed(FADEUP, 10);
1822da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                    } else {
1832da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                        mCurrentVolume = 1.0f;
184792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    }
1852da473d14a11b552621b2f3cd5bdf1758b117ed5Marco Nelissen                    mPlayer.setVolume(mCurrentVolume);
186792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    break;
187792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                case SERVER_DIED:
188c1333370c94dd708f2d1555d8aee6ddf2bc88a37Marco Nelissen                    if (mIsSupposedToBePlaying) {
189bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                        gotoNext(true);
190792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    } else {
191792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        // the server died when we were idle, so just
192792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        // reopen the same song (it will start again
193792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        // from the beginning though when the user
194792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        // restarts)
195e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                        openCurrentAndNext();
196792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    }
197792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    break;
198e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                case TRACK_WENT_TO_NEXT:
199e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    mPlayPos = mNextPlayPos;
200e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    if (mCursor != null) {
201e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                        mCursor.close();
202e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                        mCursor = null;
203e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    }
204ea5075144c4f6da8dfd743ba1deb4ec7c59ef1c0kenthin                    if (mPlayPos >= 0 && mPlayPos < mPlayList.length) {
205ea5075144c4f6da8dfd743ba1deb4ec7c59ef1c0kenthin                        mCursor = getCursorForId(mPlayList[mPlayPos]);
206ea5075144c4f6da8dfd743ba1deb4ec7c59ef1c0kenthin                    }
207e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    notifyChange(META_CHANGED);
208e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    updateNotification();
209e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    setNextTrack();
210e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    break;
211792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                case TRACK_ENDED:
212792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    if (mRepeatMode == REPEAT_CURRENT) {
213792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        seek(0);
214792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        play();
215792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    } else {
216bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                        gotoNext(false);
217792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    }
218792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    break;
219792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                case RELEASE_WAKELOCK:
220792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mWakeLock.release();
221792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    break;
2227181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen
2237181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                case FOCUSCHANGE:
2247181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                    // This code is here so we can better synchronize it with the code that
2257181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                    // handles fade-in
2267181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                    switch (msg.arg1) {
2277181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                        case AudioManager.AUDIOFOCUS_LOSS:
2287181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_LOSS");
229b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                            if (isPlaying()) {
2307181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                                mPausedByTransientLossOfFocus = false;
2317181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            }
2327181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            pause();
2337181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            break;
2347181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
2357bae28fb4233e3d028b0436fb28f783dea14927eMarco Nelissen                            mMediaplayerHandler.removeMessages(FADEUP);
2367bae28fb4233e3d028b0436fb28f783dea14927eMarco Nelissen                            mMediaplayerHandler.sendEmptyMessage(FADEDOWN);
2377bae28fb4233e3d028b0436fb28f783dea14927eMarco Nelissen                            break;
2387bae28fb4233e3d028b0436fb28f783dea14927eMarco Nelissen                        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
2397181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_LOSS_TRANSIENT");
240b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                            if (isPlaying()) {
2417181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                                mPausedByTransientLossOfFocus = true;
2427181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            }
2437181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            pause();
2447181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            break;
2457181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                        case AudioManager.AUDIOFOCUS_GAIN:
2467181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_GAIN");
247b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                            if (!isPlaying() && mPausedByTransientLossOfFocus) {
2487181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                                mPausedByTransientLossOfFocus = false;
2497181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                                mCurrentVolume = 0f;
2507181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                                mPlayer.setVolume(mCurrentVolume);
2517181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                                play(); // also queues a fade-in
2527181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            } else {
2537bae28fb4233e3d028b0436fb28f783dea14927eMarco Nelissen                                mMediaplayerHandler.removeMessages(FADEDOWN);
2547181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                                mMediaplayerHandler.sendEmptyMessage(FADEUP);
2557181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            }
2567181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            break;
2577181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                        default:
2587181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                            Log.e(LOGTAG, "Unknown audio focus change code");
2597181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                    }
2607181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen                    break;
2617181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen
262792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                default:
263792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    break;
264792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
265792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
266792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    };
267792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
268792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
269792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        @Override
270792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void onReceive(Context context, Intent intent) {
271792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            String action = intent.getAction();
272792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            String cmd = intent.getStringExtra("command");
2733988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen            MusicUtils.debugLog("mIntentReceiver.onReceive " + action + " / " + cmd);
274792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (CMDNEXT.equals(cmd) || NEXT_ACTION.equals(action)) {
275bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                gotoNext(true);
276792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } else if (CMDPREVIOUS.equals(cmd) || PREVIOUS_ACTION.equals(action)) {
277792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                prev();
278792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } else if (CMDTOGGLEPAUSE.equals(cmd) || TOGGLEPAUSE_ACTION.equals(action)) {
279792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (isPlaying()) {
280792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    pause();
281aa331877dd1a0777042481e5c925cd951682588fJean-Michel Trivi                    mPausedByTransientLossOfFocus = false;
282792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } else {
283792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    play();
284792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
285792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } else if (CMDPAUSE.equals(cmd) || PAUSE_ACTION.equals(action)) {
286792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                pause();
287aa331877dd1a0777042481e5c925cd951682588fJean-Michel Trivi                mPausedByTransientLossOfFocus = false;
288fc1c0dc301b5cae2e8c5661e7a217dc57dacac1eMarco Nelissen            } else if (CMDPLAY.equals(cmd)) {
289fc1c0dc301b5cae2e8c5661e7a217dc57dacac1eMarco Nelissen                play();
290792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } else if (CMDSTOP.equals(cmd)) {
291792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                pause();
292aa331877dd1a0777042481e5c925cd951682588fJean-Michel Trivi                mPausedByTransientLossOfFocus = false;
293792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                seek(0);
294490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project            } else if (MediaAppWidgetProvider.CMDAPPWIDGETUPDATE.equals(cmd)) {
295490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project                // Someone asked us to refresh a set of specific widgets, probably
296792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                // because they were just added.
297490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project                int[] appWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
298490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project                mAppWidgetProvider.performUpdate(MediaPlaybackService.this, appWidgetIds);
299792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
300792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
301792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    };
302792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
303085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi    private OnAudioFocusChangeListener mAudioFocusListener = new OnAudioFocusChangeListener() {
304f4cfdfd4a99040be44a1ccd74edbde762edc845bJean-Michel Trivi        public void onAudioFocusChange(int focusChange) {
3057181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen            mMediaplayerHandler.obtainMessage(FOCUSCHANGE, focusChange, 0).sendToTarget();
306085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi        }
307085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi    };
308085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi
309b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He    public MediaPlaybackService() {}
310792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
311792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    @Override
312792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void onCreate() {
313792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        super.onCreate();
314085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi
315085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
316b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        ComponentName rec =
317b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                new ComponentName(getPackageName(), MediaButtonIntentReceiver.class.getName());
3188717d3490e179e9591eac98a91a14995d98291adMarco Nelissen        mAudioManager.registerMediaButtonEventReceiver(rec);
3198ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood
3208ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood        Intent i = new Intent(Intent.ACTION_MEDIA_BUTTON);
3218ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood        i.setComponent(rec);
322b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        PendingIntent pi = PendingIntent.getBroadcast(
323b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                this /*context*/, 0 /*requestCode, ignored*/, i /*intent*/, 0 /*flags*/);
3248ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood        mRemoteControlClient = new RemoteControlClient(pi);
3258ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood        mAudioManager.registerRemoteControlClient(mRemoteControlClient);
3268ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood
3278ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood        int flags = RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS
328b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                | RemoteControlClient.FLAG_KEY_MEDIA_NEXT | RemoteControlClient.FLAG_KEY_MEDIA_PLAY
3298ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood                | RemoteControlClient.FLAG_KEY_MEDIA_PAUSE
3308ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood                | RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE
3318ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood                | RemoteControlClient.FLAG_KEY_MEDIA_STOP;
3328ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood        mRemoteControlClient.setTransportControlFlags(flags);
333b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
3346036ce6127022880a3d9c99bd15db4c968f3e6a3Dmitry Shmidt        mPreferences = getSharedPreferences("Music", MODE_PRIVATE);
33587bbf3c40cd044c3daa9fad28fb94e8570fcae05Marco Nelissen        mCardId = MusicUtils.getCardId(this);
336b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
337792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        registerExternalStorageListener();
338792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
339b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        // Needs to be done in this thread, since otherwise ApplicationContext.getPowerManager()
340b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        // crashes.
341792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mPlayer = new MultiPlayer();
342792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mPlayer.setHandler(mMediaplayerHandler);
343792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
344792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        reloadQueue();
3458717d3490e179e9591eac98a91a14995d98291adMarco Nelissen        notifyChange(QUEUE_CHANGED);
3468717d3490e179e9591eac98a91a14995d98291adMarco Nelissen        notifyChange(META_CHANGED);
3478717d3490e179e9591eac98a91a14995d98291adMarco Nelissen
348792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        IntentFilter commandFilter = new IntentFilter();
349792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        commandFilter.addAction(SERVICECMD);
350792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        commandFilter.addAction(TOGGLEPAUSE_ACTION);
351792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        commandFilter.addAction(PAUSE_ACTION);
352792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        commandFilter.addAction(NEXT_ACTION);
353792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        commandFilter.addAction(PREVIOUS_ACTION);
354792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        registerReceiver(mIntentReceiver, commandFilter);
355b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
356b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
357792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this.getClass().getName());
358792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mWakeLock.setReferenceCounted(false);
359792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
360792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // If the service was idle, but got killed before it stopped itself, the
361792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // system will relaunch it. Make sure it gets stopped again in that case.
362792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        Message msg = mDelayedStopHandler.obtainMessage();
363792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY);
364792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
365792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
366792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    @Override
367792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void onDestroy() {
368792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // Check that we're not being destroyed while something is still playing.
369792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (isPlaying()) {
370e99341f9674462607f3fd5e8dbd100b34b973e1bMarco Nelissen            Log.e(LOGTAG, "Service being destroyed while still playing.");
371792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
3722b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        // release all MediaPlayer resources, including the native player and wakelocks
373f2ef3b5153fd2f447939f267d8887ff3bba0d13fMarco Nelissen        Intent i = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
374f2ef3b5153fd2f447939f267d8887ff3bba0d13fMarco Nelissen        i.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, getAudioSessionId());
375f2ef3b5153fd2f447939f267d8887ff3bba0d13fMarco Nelissen        i.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName());
376f2ef3b5153fd2f447939f267d8887ff3bba0d13fMarco Nelissen        sendBroadcast(i);
3772b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        mPlayer.release();
3782b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        mPlayer = null;
379085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi
380085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi        mAudioManager.abandonAudioFocus(mAudioFocusListener);
3818ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood        mAudioManager.unregisterRemoteControlClient(mRemoteControlClient);
382b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
3832b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        // make sure there aren't any other messages coming
3842b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        mDelayedStopHandler.removeCallbacksAndMessages(null);
38549e36ea916aefba042c6e927c771b10708b6891eMarco Nelissen        mMediaplayerHandler.removeCallbacksAndMessages(null);
3862b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen
387792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mCursor != null) {
388792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mCursor.close();
389792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mCursor = null;
390792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
391792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
392792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        unregisterReceiver(mIntentReceiver);
393792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mUnmountReceiver != null) {
394792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            unregisterReceiver(mUnmountReceiver);
395792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mUnmountReceiver = null;
396792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
397792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mWakeLock.release();
398792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        super.onDestroy();
399792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
400b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
401b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He    private final char hexdigits[] = new char[] {
402b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
403792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
404792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private void saveQueue(boolean full) {
4058d08ec235831d71fdd7f7b6f7757c2bc19528faeMarco Nelissen        if (!mQueueIsSaveable) {
406792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return;
407792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
4088d08ec235831d71fdd7f7b6f7757c2bc19528faeMarco Nelissen
409792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        Editor ed = mPreferences.edit();
410b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        // long start = System.currentTimeMillis();
411792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (full) {
412792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            StringBuilder q = new StringBuilder();
413b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
414792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // The current playlist is saved as a list of "reverse hexadecimal"
415792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // numbers, which we can generate faster than normal decimal or
416792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // hexadecimal numbers, which in turn allows us to save the playlist
417792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // more often without worrying too much about performance.
418792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // (saving the full state takes about 40 ms under no-load conditions
419792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // on the phone)
420792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            int len = mPlayListLen;
421792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            for (int i = 0; i < len; i++) {
422bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen                long n = mPlayList[i];
423133550210e448fe52698f6cd0e80e07b8cc480bdMarco Nelissen                if (n < 0) {
424133550210e448fe52698f6cd0e80e07b8cc480bdMarco Nelissen                    continue;
425133550210e448fe52698f6cd0e80e07b8cc480bdMarco Nelissen                } else if (n == 0) {
426792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    q.append("0;");
427792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } else {
428792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    while (n != 0) {
429b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                        int digit = (int) (n & 0xf);
430133550210e448fe52698f6cd0e80e07b8cc480bdMarco Nelissen                        n >>>= 4;
431792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        q.append(hexdigits[digit]);
432792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    }
433792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    q.append(";");
434792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
435792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
436b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            // Log.i("@@@@ service", "created queue string in " + (System.currentTimeMillis() -
437b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            // start) + " ms");
438792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            ed.putString("queue", q.toString());
439792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            ed.putInt("cardid", mCardId);
44063dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen            if (mShuffleMode != SHUFFLE_NONE) {
44163dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                // In shuffle mode we need to save the history too
44263dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                len = mHistory.size();
44363dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                q.setLength(0);
44463dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                for (int i = 0; i < len; i++) {
44563dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                    int n = mHistory.get(i);
44663dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                    if (n == 0) {
44763dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                        q.append("0;");
44863dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                    } else {
44963dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                        while (n != 0) {
45063dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            int digit = (n & 0xf);
451133550210e448fe52698f6cd0e80e07b8cc480bdMarco Nelissen                            n >>>= 4;
45263dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            q.append(hexdigits[digit]);
45363dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                        }
45463dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                        q.append(";");
45563dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                    }
45663dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                }
45763dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                ed.putString("history", q.toString());
45863dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen            }
459792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
460792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        ed.putInt("curpos", mPlayPos);
461792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mPlayer.isInitialized()) {
462792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            ed.putLong("seekpos", mPlayer.position());
463792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
464792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        ed.putInt("repeatmode", mRepeatMode);
465792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        ed.putInt("shufflemode", mShuffleMode);
46614c3caeb7bc2de0dd7abbb1e5f217dbb6367afbaBrad Fitzpatrick        SharedPreferencesCompat.apply(ed);
46714c3caeb7bc2de0dd7abbb1e5f217dbb6367afbaBrad Fitzpatrick
468b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        // Log.i("@@@@ service", "saved state in " + (System.currentTimeMillis() - start) + " ms");
469792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
470792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
471792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private void reloadQueue() {
472792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        String q = null;
473b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
474792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        boolean newstyle = false;
475792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        int id = mCardId;
476792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mPreferences.contains("cardid")) {
477792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            newstyle = true;
478792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            id = mPreferences.getInt("cardid", ~mCardId);
479792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
480792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (id == mCardId) {
481792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // Only restore the saved playlist if the card is still
482792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // the same one as when the playlist was saved
483792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            q = mPreferences.getString("queue", "");
484792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
4856c615a208d88fe49c7da43c59720f9db6ca1e964Marco Nelissen        int qlen = q != null ? q.length() : 0;
4866c615a208d88fe49c7da43c59720f9db6ca1e964Marco Nelissen        if (qlen > 1) {
487b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            // Log.i("@@@@ service", "loaded queue: " + q);
488bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen            int plen = 0;
489bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen            int n = 0;
490bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen            int shift = 0;
491bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen            for (int i = 0; i < qlen; i++) {
492bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                char c = q.charAt(i);
493bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                if (c == ';') {
494bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                    ensurePlayListCapacity(plen + 1);
495bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                    mPlayList[plen] = n;
496bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                    plen++;
497bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                    n = 0;
498bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                    shift = 0;
499792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } else {
500bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                    if (c >= '0' && c <= '9') {
501bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                        n += ((c - '0') << shift);
502bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                    } else if (c >= 'a' && c <= 'f') {
503bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                        n += ((10 + c - 'a') << shift);
504bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                    } else {
505bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                        // bogus playlist data
506bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                        plen = 0;
507bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                        break;
508bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                    }
509bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen                    shift += 4;
510792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
511792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
512bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen            mPlayListLen = plen;
513792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
514792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            int pos = mPreferences.getInt("curpos", 0);
515bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen            if (pos < 0 || pos >= mPlayListLen) {
516792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                // The saved playlist is bogus, discard it
517792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayListLen = 0;
518792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return;
519792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
520792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mPlayPos = pos;
521b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
522792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // When reloadQueue is called in response to a card-insertion,
523792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // we might not be able to query the media provider right away.
524792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // To deal with this, try querying for the current file, and if
525792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // that fails, wait a while and try again. If that too fails,
526792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // assume there is a problem and don't restore the state.
527b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            Cursor crsr = MusicUtils.query(this, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
528b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                    new String[] {"_id"}, "_id=" + mPlayList[mPlayPos], null, null);
52963dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen            if (crsr == null || crsr.getCount() == 0) {
530792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                // wait a bit and try again
531792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                SystemClock.sleep(3000);
532b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                crsr = getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
533b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                        mCursorCols, "_id=" + mPlayList[mPlayPos], null, null);
534792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
53563dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen            if (crsr != null) {
53663dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                crsr.close();
537792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
538792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
539792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // Make sure we don't auto-skip to the next song, since that
540792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // also starts playback. What could happen in that case is:
541792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // - music is paused
542792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // - go to UMS and delete some files, including the currently playing one
543792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // - come back from UMS
544792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // (time passes)
545792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // - music app is killed for some reason (out of memory)
546792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // - music service is restarted, service restores state, doesn't find
547792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            //   the "current" file, goes to the next and: playback starts on its
548792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            //   own, potentially at some random inconvenient time.
549792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mOpenFailedCounter = 20;
550792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mQuietMode = true;
551e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            openCurrentAndNext();
552792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mQuietMode = false;
553792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (!mPlayer.isInitialized()) {
554792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                // couldn't restore the saved state
555792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayListLen = 0;
556792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return;
557792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
558b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
559792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            long seekpos = mPreferences.getLong("seekpos", 0);
560792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            seek(seekpos >= 0 && seekpos < duration() ? seekpos : 0);
561b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            Log.d(LOGTAG, "restored queue, currently at position " + position() + "/" + duration()
562b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                            + " (requested " + seekpos + ")");
563b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
564792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            int repmode = mPreferences.getInt("repeatmode", REPEAT_NONE);
565792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (repmode != REPEAT_ALL && repmode != REPEAT_CURRENT) {
566792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                repmode = REPEAT_NONE;
567792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
568792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mRepeatMode = repmode;
569792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
570792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            int shufmode = mPreferences.getInt("shufflemode", SHUFFLE_NONE);
571792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (shufmode != SHUFFLE_AUTO && shufmode != SHUFFLE_NORMAL) {
572792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                shufmode = SHUFFLE_NONE;
573792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
57463dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen            if (shufmode != SHUFFLE_NONE) {
57563dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                // in shuffle mode we need to restore the history too
57663dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                q = mPreferences.getString("history", "");
57763dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                qlen = q != null ? q.length() : 0;
57863dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                if (qlen > 1) {
57963dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                    plen = 0;
58063dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                    n = 0;
58163dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                    shift = 0;
58263dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                    mHistory.clear();
58363dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                    for (int i = 0; i < qlen; i++) {
58463dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                        char c = q.charAt(i);
58563dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                        if (c == ';') {
58663dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            if (n >= mPlayListLen) {
58763dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                                // bogus history data
58863dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                                mHistory.clear();
58963dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                                break;
59063dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            }
59163dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            mHistory.add(n);
59263dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            n = 0;
59363dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            shift = 0;
59463dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                        } else {
59563dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            if (c >= '0' && c <= '9') {
59663dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                                n += ((c - '0') << shift);
59763dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            } else if (c >= 'a' && c <= 'f') {
59863dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                                n += ((10 + c - 'a') << shift);
59963dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            } else {
60063dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                                // bogus history data
60163dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                                mHistory.clear();
60263dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                                break;
60363dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            }
60463dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                            shift += 4;
60563dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                        }
60663dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                    }
60763dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen                }
60863dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen            }
609792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (shufmode == SHUFFLE_AUTO) {
610b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                if (!makeAutoShuffleList()) {
611792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    shufmode = SHUFFLE_NONE;
612792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
613792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
614792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mShuffleMode = shufmode;
615792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
616792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
617b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
618792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    @Override
619792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public IBinder onBind(Intent intent) {
620792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mDelayedStopHandler.removeCallbacksAndMessages(null);
621792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mServiceInUse = true;
622792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return mBinder;
623792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
624792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
625792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    @Override
626792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void onRebind(Intent intent) {
627792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mDelayedStopHandler.removeCallbacksAndMessages(null);
628792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mServiceInUse = true;
629792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
630792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
631792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    @Override
632c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen    public int onStartCommand(Intent intent, int flags, int startId) {
633792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mServiceStartId = startId;
634792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mDelayedStopHandler.removeCallbacksAndMessages(null);
635c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen
636c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen        if (intent != null) {
637c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen            String action = intent.getAction();
638c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen            String cmd = intent.getStringExtra("command");
6393988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen            MusicUtils.debugLog("onStartCommand " + action + " / " + cmd);
640c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen
641c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen            if (CMDNEXT.equals(cmd) || NEXT_ACTION.equals(action)) {
642bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                gotoNext(true);
643c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen            } else if (CMDPREVIOUS.equals(cmd) || PREVIOUS_ACTION.equals(action)) {
644b63b5d1b6cae1cc9e28ba63b316fc15da5e16939Marco Nelissen                if (position() < 2000) {
645b63b5d1b6cae1cc9e28ba63b316fc15da5e16939Marco Nelissen                    prev();
646b63b5d1b6cae1cc9e28ba63b316fc15da5e16939Marco Nelissen                } else {
647b63b5d1b6cae1cc9e28ba63b316fc15da5e16939Marco Nelissen                    seek(0);
648b63b5d1b6cae1cc9e28ba63b316fc15da5e16939Marco Nelissen                    play();
649b63b5d1b6cae1cc9e28ba63b316fc15da5e16939Marco Nelissen                }
650c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen            } else if (CMDTOGGLEPAUSE.equals(cmd) || TOGGLEPAUSE_ACTION.equals(action)) {
651c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen                if (isPlaying()) {
652c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen                    pause();
653aa331877dd1a0777042481e5c925cd951682588fJean-Michel Trivi                    mPausedByTransientLossOfFocus = false;
654c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen                } else {
655c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen                    play();
656c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen                }
657c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen            } else if (CMDPAUSE.equals(cmd) || PAUSE_ACTION.equals(action)) {
658792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                pause();
659aa331877dd1a0777042481e5c925cd951682588fJean-Michel Trivi                mPausedByTransientLossOfFocus = false;
66047dd847ba35375d95c15bc1ee141d638f03ec82eJaikumar Ganesh            } else if (CMDPLAY.equals(cmd)) {
66147dd847ba35375d95c15bc1ee141d638f03ec82eJaikumar Ganesh                play();
662c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen            } else if (CMDSTOP.equals(cmd)) {
663c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen                pause();
664aa331877dd1a0777042481e5c925cd951682588fJean-Michel Trivi                mPausedByTransientLossOfFocus = false;
665c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen                seek(0);
666792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
667792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
668b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
669792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // make sure the service will shut down on its own if it was
670792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // just started but not bound to and nothing is playing
671792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mDelayedStopHandler.removeCallbacksAndMessages(null);
672792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        Message msg = mDelayedStopHandler.obtainMessage();
673792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY);
674c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen        return START_STICKY;
675792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
676b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
677792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    @Override
678792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public boolean onUnbind(Intent intent) {
679792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mServiceInUse = false;
680792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
681792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // Take a snapshot of the current playlist
682792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        saveQueue(true);
683792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
684aa331877dd1a0777042481e5c925cd951682588fJean-Michel Trivi        if (isPlaying() || mPausedByTransientLossOfFocus) {
685b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            // something is currently playing, or will be playing once
686aa331877dd1a0777042481e5c925cd951682588fJean-Michel Trivi            // an in-progress action requesting audio focus ends, so don't stop the service now.
687792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return true;
688792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
689b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
690792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // If there is a playlist but playback is paused, then wait a while
691792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // before stopping the service, so that pause/resume isn't slow.
692792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // Also delay stopping the service if we're transitioning between tracks.
693b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        if (mPlayListLen > 0 || mMediaplayerHandler.hasMessages(TRACK_ENDED)) {
694792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            Message msg = mDelayedStopHandler.obtainMessage();
695792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY);
696792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return true;
697792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
698b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
699792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // No active playlist, OK to stop the service right now
700792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        stopSelf(mServiceStartId);
701792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return true;
702792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
703b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
704792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private Handler mDelayedStopHandler = new Handler() {
705792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        @Override
706792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void handleMessage(Message msg) {
707792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // Check again to make sure nothing is playing right now
708aa331877dd1a0777042481e5c925cd951682588fJean-Michel Trivi            if (isPlaying() || mPausedByTransientLossOfFocus || mServiceInUse
709792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    || mMediaplayerHandler.hasMessages(TRACK_ENDED)) {
710792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return;
711792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
712792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // save the queue again, because it might have changed
713792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // since the user exited the music app (because of
714792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // party-shuffle or because the play-position changed)
715792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            saveQueue(true);
716792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            stopSelf(mServiceStartId);
717792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
718792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    };
719f2ef3b5153fd2f447939f267d8887ff3bba0d13fMarco Nelissen
720792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
721792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Called when we receive a ACTION_MEDIA_EJECT notification.
722792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     *
723792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param storagePath path to mount point for the removed media
724792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
725792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void closeExternalStorageFiles(String storagePath) {
726792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // stop playback and clean up if the SD card is going to be unmounted.
727792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        stop(true);
728792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        notifyChange(QUEUE_CHANGED);
729792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        notifyChange(META_CHANGED);
730792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
731792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
732792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
733792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Registers an intent to listen for ACTION_MEDIA_EJECT notifications.
734792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * The intent will call closeExternalStorageFiles() if the external media
735792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * is going to be ejected, so applications can clean up any files they have open.
736792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
737792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void registerExternalStorageListener() {
738792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mUnmountReceiver == null) {
739792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mUnmountReceiver = new BroadcastReceiver() {
740792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                @Override
741792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                public void onReceive(Context context, Intent intent) {
742792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    String action = intent.getAction();
743792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    if (action.equals(Intent.ACTION_MEDIA_EJECT)) {
744792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        saveQueue(true);
7458d08ec235831d71fdd7f7b6f7757c2bc19528faeMarco Nelissen                        mQueueIsSaveable = false;
746792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        closeExternalStorageFiles(intent.getData().getPath());
747792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    } else if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
748792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        mMediaMountedCount++;
74987bbf3c40cd044c3daa9fad28fb94e8570fcae05Marco Nelissen                        mCardId = MusicUtils.getCardId(MediaPlaybackService.this);
750792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        reloadQueue();
7518d08ec235831d71fdd7f7b6f7757c2bc19528faeMarco Nelissen                        mQueueIsSaveable = true;
752792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        notifyChange(QUEUE_CHANGED);
753792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        notifyChange(META_CHANGED);
754792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    }
755792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
756792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            };
757792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            IntentFilter iFilter = new IntentFilter();
758792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            iFilter.addAction(Intent.ACTION_MEDIA_EJECT);
759792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            iFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
760792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            iFilter.addDataScheme("file");
761792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            registerReceiver(mUnmountReceiver, iFilter);
762792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
763792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
764792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
765792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
766792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Notify the change-receivers that something has changed.
767792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * The intent that is sent contains the following data
768792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * for the currently playing track:
769792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * "id" - Integer: the database row ID
770792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * "artist" - String: the name of the artist
771792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * "album" - String: the name of the album
772792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * "track" - String: the name of the track
773792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * The intent has an action that is one of
774792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * "com.android.music.metachanged"
775792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * "com.android.music.queuechanged",
776792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * "com.android.music.playbackcomplete"
777792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * "com.android.music.playstatechanged"
778792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * respectively indicating that a new track has
779792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * started playing, that the playback queue has
780792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * changed, that playback has stopped because
781792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * the last file in the list has been played,
782792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * or that the play-state changed (paused/resumed).
783792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
784792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private void notifyChange(String what) {
785792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        Intent i = new Intent(what);
786bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen        i.putExtra("id", Long.valueOf(getAudioId()));
787792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        i.putExtra("artist", getArtistName());
788b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        i.putExtra("album", getAlbumName());
789792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        i.putExtra("track", getTrackName());
7906b507de7beda7f4a8b969b437dfbf81f5ecd5e5eMarco Nelissen        i.putExtra("playing", isPlaying());
7916b507de7beda7f4a8b969b437dfbf81f5ecd5e5eMarco Nelissen        sendStickyBroadcast(i);
7928717d3490e179e9591eac98a91a14995d98291adMarco Nelissen
7938717d3490e179e9591eac98a91a14995d98291adMarco Nelissen        if (what.equals(PLAYSTATE_CHANGED)) {
794b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            mRemoteControlClient.setPlaybackState(isPlaying()
795b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                            ? RemoteControlClient.PLAYSTATE_PLAYING
796b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                            : RemoteControlClient.PLAYSTATE_PAUSED);
7978717d3490e179e9591eac98a91a14995d98291adMarco Nelissen        } else if (what.equals(META_CHANGED)) {
7988ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood            RemoteControlClient.MetadataEditor ed = mRemoteControlClient.editMetadata(true);
7998ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood            ed.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, getTrackName());
8008ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood            ed.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, getAlbumName());
8018ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood            ed.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, getArtistName());
8028ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood            ed.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, duration());
8038ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood            Bitmap b = MusicUtils.getArtwork(this, getAudioId(), getAlbumId(), false);
8048ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood            if (b != null) {
8058ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood                ed.putBitmap(MetadataEditor.BITMAP_KEY_ARTWORK, b);
8068ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood            }
8078ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood            ed.apply();
8088717d3490e179e9591eac98a91a14995d98291adMarco Nelissen        }
8098717d3490e179e9591eac98a91a14995d98291adMarco Nelissen
810792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (what.equals(QUEUE_CHANGED)) {
811792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            saveQueue(true);
812792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        } else {
813792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            saveQueue(false);
814792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
815b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
816490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project        // Share this notification directly with our widgets
817490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project        mAppWidgetProvider.notifyChange(this, what);
818792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
819792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
820792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private void ensurePlayListCapacity(int size) {
821792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mPlayList == null || size > mPlayList.length) {
822792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // reallocate at 2x requested size so we don't
823792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // need to grow and copy the array for every
824792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // insert
825b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            long[] newlist = new long[size * 2];
826bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen            int len = mPlayList != null ? mPlayList.length : mPlayListLen;
827792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            for (int i = 0; i < len; i++) {
828792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                newlist[i] = mPlayList[i];
829792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
830792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mPlayList = newlist;
831792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
832792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // FIXME: shrink the array when the needed size is much smaller
833792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // than the allocated size
834792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
835b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
836792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    // insert the list of songs at the specified position in the playlist
837b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He    private void addToPlayList(long[] list, int position) {
838792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        int addlen = list.length;
839792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (position < 0) { // overwrite
840792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mPlayListLen = 0;
841792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            position = 0;
842792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
843792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        ensurePlayListCapacity(mPlayListLen + addlen);
844792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (position > mPlayListLen) {
845792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            position = mPlayListLen;
846792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
847b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
848792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // move part of list after insertion point
849792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        int tailsize = mPlayListLen - position;
850b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        for (int i = tailsize; i > 0; i--) {
851b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            mPlayList[position + i] = mPlayList[position + i - addlen];
852792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
853b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
854792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // copy list into playlist
855792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        for (int i = 0; i < addlen; i++) {
856792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mPlayList[position + i] = list[i];
857792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
858792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mPlayListLen += addlen;
8593aa9ad029bc85da2e07bba019cec928ad5d74043Marco Nelissen        if (mPlayListLen == 0) {
8603aa9ad029bc85da2e07bba019cec928ad5d74043Marco Nelissen            mCursor.close();
8613aa9ad029bc85da2e07bba019cec928ad5d74043Marco Nelissen            mCursor = null;
8623aa9ad029bc85da2e07bba019cec928ad5d74043Marco Nelissen            notifyChange(META_CHANGED);
8633aa9ad029bc85da2e07bba019cec928ad5d74043Marco Nelissen        }
864792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
865b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
866792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
867792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Appends a list of tracks to the current playlist.
868792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * If nothing is playing currently, playback will be started at
869792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * the first track.
870792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * If the action is NOW, playback will switch to the first of
871792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * the new tracks immediately.
872792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param list The list of tracks to append.
873792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param action NOW, NEXT or LAST
874792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
875b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He    public void enqueue(long[] list, int action) {
876b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        synchronized (this) {
877792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (action == NEXT && mPlayPos + 1 < mPlayListLen) {
878792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                addToPlayList(list, mPlayPos + 1);
879792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                notifyChange(QUEUE_CHANGED);
880792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } else {
881792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                // action == LAST || action == NOW || mPlayPos + 1 == mPlayListLen
882792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                addToPlayList(list, Integer.MAX_VALUE);
883792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                notifyChange(QUEUE_CHANGED);
884792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (action == NOW) {
885792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mPlayPos = mPlayListLen - list.length;
886e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    openCurrentAndNext();
887792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    play();
888792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    notifyChange(META_CHANGED);
889792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    return;
890792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
891792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
892792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mPlayPos < 0) {
893792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayPos = 0;
894e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                openCurrentAndNext();
895792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                play();
896792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                notifyChange(META_CHANGED);
897792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
898792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
899792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
900792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
901792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
902792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Replaces the current playlist with a new list,
903792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * and prepares for starting playback at the specified
904792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * position in the list, or a random position if the
905792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * specified position is 0.
906792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param list The new list of tracks.
907792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
908b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He    public void open(long[] list, int position) {
909792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
910792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mShuffleMode == SHUFFLE_AUTO) {
911792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mShuffleMode = SHUFFLE_NORMAL;
912792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
913bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen            long oldId = getAudioId();
914792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            int listlength = list.length;
915792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            boolean newlist = true;
916792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mPlayListLen == listlength) {
917792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                // possible fast path: list might be the same
918792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                newlist = false;
919792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                for (int i = 0; i < listlength; i++) {
920792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    if (list[i] != mPlayList[i]) {
921792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        newlist = true;
922792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        break;
923792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    }
924792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
925792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
926792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (newlist) {
927792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                addToPlayList(list, -1);
928792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                notifyChange(QUEUE_CHANGED);
929792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
930792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            int oldpos = mPlayPos;
931792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (position >= 0) {
932792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayPos = position;
933792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } else {
934792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayPos = mRand.nextInt(mPlayListLen);
935792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
936792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mHistory.clear();
937792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
938792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            saveBookmarkIfNeeded();
939e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            openCurrentAndNext();
940d8c696753687ddb0177f869fad5f709eadfe27afJeffrey Sharkey            if (oldId != getAudioId()) {
941792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                notifyChange(META_CHANGED);
942792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
943792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
944792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
945b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
946792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
947792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Moves the item at index1 to index2.
948792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param index1
949792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param index2
950792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
951792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void moveQueueItem(int index1, int index2) {
952792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
953792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (index1 >= mPlayListLen) {
954792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                index1 = mPlayListLen - 1;
955792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
956792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (index2 >= mPlayListLen) {
957792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                index2 = mPlayListLen - 1;
958792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
959792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (index1 < index2) {
960bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen                long tmp = mPlayList[index1];
961792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                for (int i = index1; i < index2; i++) {
962b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                    mPlayList[i] = mPlayList[i + 1];
963792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
964792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayList[index2] = tmp;
965792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (mPlayPos == index1) {
966792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mPlayPos = index2;
967792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } else if (mPlayPos >= index1 && mPlayPos <= index2) {
968b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                    mPlayPos--;
969792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
970792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } else if (index2 < index1) {
971bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen                long tmp = mPlayList[index1];
972792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                for (int i = index1; i > index2; i--) {
973b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                    mPlayList[i] = mPlayList[i - 1];
974792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
975792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayList[index2] = tmp;
976792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (mPlayPos == index1) {
977792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mPlayPos = index2;
978792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } else if (mPlayPos >= index2 && mPlayPos <= index1) {
979b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                    mPlayPos++;
980792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
981792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
982792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            notifyChange(QUEUE_CHANGED);
983792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
984792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
985792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
986792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
987792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Returns the current play list
988792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @return An array of integers containing the IDs of the tracks in the play list
989792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
990b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He    public long[] getQueue() {
991792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
992792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            int len = mPlayListLen;
993b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            long[] list = new long[len];
994792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            for (int i = 0; i < len; i++) {
995792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                list[i] = mPlayList[i];
996792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
997792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return list;
998792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
999792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1000792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1001e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen    private Cursor getCursorForId(long lid) {
1002e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        String id = String.valueOf(lid);
1003e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen
1004e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        Cursor c = getContentResolver().query(
1005b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, mCursorCols, "_id=" + id, null, null);
1006c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen        if (c != null) {
1007c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen            c.moveToFirst();
1008c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen        }
1009e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        return c;
1010e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen    }
1011e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen
1012e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen    private void openCurrentAndNext() {
1013792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1014792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mCursor != null) {
1015792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mCursor.close();
1016792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mCursor = null;
1017792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
10188d08ec235831d71fdd7f7b6f7757c2bc19528faeMarco Nelissen
1019792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mPlayListLen == 0) {
1020792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return;
1021792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1022792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            stop(false);
1023792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
102490d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            mCursor = getCursorForId(mPlayList[mPlayPos]);
1025b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            while (true) {
1026b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                if (mCursor != null && mCursor.getCount() != 0
1027b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                        && open(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "/"
1028b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                                   + mCursor.getLong(IDCOLIDX))) {
1029c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                    break;
1030c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                }
1031c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                // if we get here then opening the file failed. We can close the cursor now, because
1032c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                // we're either going to create a new one next, or stop trying
1033c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                if (mCursor != null) {
1034c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                    mCursor.close();
1035c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                    mCursor = null;
10366fb8551aad7f7af3f099dfa286df587a31484000Marco Nelissen                }
1037b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                if (mOpenFailedCounter++ < 10 && mPlayListLen > 1) {
103890d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    int pos = getNextPosition(false);
103990d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    if (pos < 0) {
104090d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                        gotoIdleState();
104190d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                        if (mIsSupposedToBePlaying) {
104290d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                            mIsSupposedToBePlaying = false;
104390d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                            notifyChange(PLAYSTATE_CHANGED);
104490d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                        }
104590d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                        return;
104690d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    }
104790d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    mPlayPos = pos;
104890d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    stop(false);
104990d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    mPlayPos = pos;
105090d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    mCursor = getCursorForId(mPlayList[mPlayPos]);
105190d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                } else {
105290d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    mOpenFailedCounter = 0;
105390d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    if (!mQuietMode) {
105490d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                        Toast.makeText(this, R.string.playback_failed, Toast.LENGTH_SHORT).show();
105590d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    }
105690d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    Log.d(LOGTAG, "Failed to open file for playback");
1057c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                    gotoIdleState();
1058c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                    if (mIsSupposedToBePlaying) {
1059c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                        mIsSupposedToBePlaying = false;
1060c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                        notifyChange(PLAYSTATE_CHANGED);
1061c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen                    }
106290d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                    return;
1063e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                }
1064792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
106590d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen
106690d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            // go to bookmark if needed
106790d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            if (isPodcast()) {
106890d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                long bookmark = getBookmark();
106990d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                // Start playing a little bit before the bookmark,
107090d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                // so it's easier to get back in to the narrative.
107190d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                seek(bookmark - 5000);
107290d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            }
107390d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            setNextTrack();
1074792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1075792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1076792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1077e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen    private void setNextTrack() {
1078e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        mNextPlayPos = getNextPosition(false);
1079e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        if (mNextPlayPos >= 0) {
1080e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            long id = mPlayList[mNextPlayPos];
1081e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mPlayer.setNextDataSource(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "/" + id);
10820a7f03fa179055114f63bf4e5b1535aefc65066fJun Tian        } else {
10830a7f03fa179055114f63bf4e5b1535aefc65066fJun Tian            mPlayer.setNextDataSource(null);
1084e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        }
1085e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen    }
1086e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen
1087792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1088792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Opens the specified file and readies it for playback.
1089792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     *
1090792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param path The full path of the file to be opened.
1091792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
109290d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen    public boolean open(String path) {
1093792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1094792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (path == null) {
109590d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                return false;
1096792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1097b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
1098792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // if mCursor is null, try to associate path with a database cursor
1099792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mCursor == null) {
1100792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                ContentResolver resolver = getContentResolver();
1101792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                Uri uri;
1102792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                String where;
1103792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                String selectionArgs[];
1104792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (path.startsWith("content://media/")) {
1105792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    uri = Uri.parse(path);
1106792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    where = null;
1107792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    selectionArgs = null;
1108792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } else {
1109b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                    uri = MediaStore.Audio.Media.getContentUriForPath(path);
1110b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                    where = MediaStore.Audio.Media.DATA + "=?";
1111b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                    selectionArgs = new String[] {path};
1112792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1113b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
1114792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                try {
1115792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mCursor = resolver.query(uri, mCursorCols, where, selectionArgs, null);
1116b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                    if (mCursor != null) {
1117792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        if (mCursor.getCount() == 0) {
1118792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                            mCursor.close();
1119792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                            mCursor = null;
1120792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        } else {
1121792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                            mCursor.moveToNext();
1122792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                            ensurePlayListCapacity(1);
1123792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                            mPlayListLen = 1;
1124bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen                            mPlayList[0] = mCursor.getLong(IDCOLIDX);
1125792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                            mPlayPos = 0;
1126792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        }
1127792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    }
1128792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } catch (UnsupportedOperationException ex) {
1129792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1130792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1131792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mFileToPlay = path;
1132792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mPlayer.setDataSource(mFileToPlay);
113390d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            if (mPlayer.isInitialized()) {
1134792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mOpenFailedCounter = 0;
113590d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                return true;
1136792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
113790d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            stop(true);
113890d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            return false;
1139792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1140792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1141792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1142792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1143792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Starts playback of a previously opened file.
1144792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1145792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void play() {
1146b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        mAudioManager.requestAudioFocus(
1147b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                mAudioFocusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
1148b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        mAudioManager.registerMediaButtonEventReceiver(new ComponentName(
1149b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                this.getPackageName(), MediaButtonIntentReceiver.class.getName()));
1150085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi
1151792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mPlayer.isInitialized()) {
1152272eb78d8836f969f7480454167bdffc6a6f6dd7Thomas Tuttle            // if we are at the end of the song, go to the next song first
11532f9a1ce2f3b0ae72d698c240ce8448bc7cfe3a77Marco Nelissen            long duration = mPlayer.duration();
1154b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            if (mRepeatMode != REPEAT_CURRENT && duration > 2000
1155b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                    && mPlayer.position() >= duration - 2000) {
1156bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                gotoNext(true);
1157272eb78d8836f969f7480454167bdffc6a6f6dd7Thomas Tuttle            }
1158272eb78d8836f969f7480454167bdffc6a6f6dd7Thomas Tuttle
1159792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mPlayer.start();
11607181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen            // make sure we fade in, in case a previous fadein was stopped because
11617181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen            // of another focus loss
11627bae28fb4233e3d028b0436fb28f783dea14927eMarco Nelissen            mMediaplayerHandler.removeMessages(FADEDOWN);
11637181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen            mMediaplayerHandler.sendEmptyMessage(FADEUP);
1164792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1165e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            updateNotification();
1166c1333370c94dd708f2d1555d8aee6ddf2bc88a37Marco Nelissen            if (!mIsSupposedToBePlaying) {
1167347fe57e4c8a860dfbad6f0ec1991171bfa69d77Mike Cleron                mIsSupposedToBePlaying = true;
1168792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                notifyChange(PLAYSTATE_CHANGED);
1169792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1170347fe57e4c8a860dfbad6f0ec1991171bfa69d77Mike Cleron
1171792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        } else if (mPlayListLen <= 0) {
1172792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // This is mostly so that if you press 'play' on a bluetooth headset
1173792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // without every having played anything before, it will still play
1174792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // something.
1175792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            setShuffleMode(SHUFFLE_AUTO);
1176792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1177792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1178e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen
1179e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen    private void updateNotification() {
1180e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        RemoteViews views = new RemoteViews(getPackageName(), R.layout.statusbar);
1181e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        views.setImageViewResource(R.id.icon, R.drawable.stat_notify_musicplayer);
1182e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        if (getAudioId() < 0) {
1183e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            // streaming
1184e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            views.setTextViewText(R.id.trackname, getPath());
1185e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            views.setTextViewText(R.id.artistalbum, null);
1186e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        } else {
1187e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            String artist = getArtistName();
1188e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            views.setTextViewText(R.id.trackname, getTrackName());
1189e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            if (artist == null || artist.equals(MediaStore.UNKNOWN_STRING)) {
1190e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                artist = getString(R.string.unknown_artist_name);
1191e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            }
1192e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            String album = getAlbumName();
1193e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            if (album == null || album.equals(MediaStore.UNKNOWN_STRING)) {
1194e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                album = getString(R.string.unknown_album_name);
1195e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            }
1196e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen
1197b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            views.setTextViewText(
1198b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                    R.id.artistalbum, getString(R.string.notification_artist_album, artist, album));
1199e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        }
1200e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        Notification status = new Notification();
1201e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        status.contentView = views;
1202e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        status.flags |= Notification.FLAG_ONGOING_EVENT;
1203e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        status.icon = R.drawable.stat_notify_musicplayer;
1204b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        status.contentIntent =
1205b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                PendingIntent.getActivity(this, 0, new Intent("com.android.music.PLAYBACK_VIEWER")
1206b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                                                           .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
1207b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                        0);
1208e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        startForeground(PLAYBACKSERVICE_STATUS, status);
1209e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen    }
1210e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen
1211792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private void stop(boolean remove_status_icon) {
1212b1c285c7e2da5492b4224f3e66ea5696b6df5fa8Glenn Kasten        if (mPlayer != null && mPlayer.isInitialized()) {
1213792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mPlayer.stop();
1214792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1215792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mFileToPlay = null;
1216792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mCursor != null) {
1217792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mCursor.close();
1218792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mCursor = null;
1219792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1220792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (remove_status_icon) {
1221792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            gotoIdleState();
1222d5fc5b6cc39bc2c2693428fe957bb4ae7f90c0f7Dianne Hackborn        } else {
1223d5fc5b6cc39bc2c2693428fe957bb4ae7f90c0f7Dianne Hackborn            stopForeground(false);
1224792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1225792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (remove_status_icon) {
1226c1333370c94dd708f2d1555d8aee6ddf2bc88a37Marco Nelissen            mIsSupposedToBePlaying = false;
1227792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1228792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1229792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1230792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1231792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Stops playback.
1232792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1233792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void stop() {
1234792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        stop(true);
1235792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1236792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1237792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1238792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Pauses playback (call play() to resume)
1239792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1240792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void pause() {
1241b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        synchronized (this) {
12427181da85214e1527bba9e7983a3a27ed98bba91bMarco Nelissen            mMediaplayerHandler.removeMessages(FADEUP);
1243407cf91b661ceeb3ced16dc20af8243b9a0eb632Marco Nelissen            if (isPlaying()) {
1244407cf91b661ceeb3ced16dc20af8243b9a0eb632Marco Nelissen                mPlayer.pause();
1245407cf91b661ceeb3ced16dc20af8243b9a0eb632Marco Nelissen                gotoIdleState();
1246407cf91b661ceeb3ced16dc20af8243b9a0eb632Marco Nelissen                mIsSupposedToBePlaying = false;
1247407cf91b661ceeb3ced16dc20af8243b9a0eb632Marco Nelissen                notifyChange(PLAYSTATE_CHANGED);
1248407cf91b661ceeb3ced16dc20af8243b9a0eb632Marco Nelissen                saveBookmarkIfNeeded();
1249407cf91b661ceeb3ced16dc20af8243b9a0eb632Marco Nelissen            }
1250792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1251792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1252792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1253b6e7bf7ddcc861e81219f9869b5dddc101341458Marco Nelissen    /** Returns whether something is currently playing
1254792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     *
1255b6e7bf7ddcc861e81219f9869b5dddc101341458Marco Nelissen     * @return true if something is playing (or will be playing shortly, in case
1256b6e7bf7ddcc861e81219f9869b5dddc101341458Marco Nelissen     * we're currently transitioning between tracks), false if not.
1257792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1258792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public boolean isPlaying() {
1259c1333370c94dd708f2d1555d8aee6ddf2bc88a37Marco Nelissen        return mIsSupposedToBePlaying;
1260792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1261792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1262792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /*
1263792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project      Desired behavior for prev/next/shuffle:
1264792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1265792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project      - NEXT will move to the next track in the list when not shuffling, and to
1266792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        a track randomly picked from the not-yet-played tracks when shuffling.
1267792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        If all tracks have already been played, pick from the full set, but
1268792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        avoid picking the previously played track if possible.
1269792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project      - when shuffling, PREV will go to the previously played track. Hitting PREV
1270792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        again will go to the track played before that, etc. When the start of the
1271792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        history has been reached, PREV is a no-op.
1272792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        When not shuffling, PREV will go to the sequentially previous track (the
1273792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        difference with the shuffle-case is mainly that when not shuffling, the
1274792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        user can back up to tracks that are not in the history).
1275792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1276792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        Example:
1277792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        When playing an album with 10 tracks from the start, and enabling shuffle
1278792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        while playing track 5, the remaining tracks (6-10) will be shuffled, e.g.
1279792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        the final play order might be 1-2-3-4-5-8-10-6-9-7.
1280792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        When hitting 'prev' 8 times while playing track 7 in this example, the
1281792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        user will go to tracks 9-6-10-8-5-4-3-2. If the user then hits 'next',
1282792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        a random track will be picked again. If at any time user disables shuffling
1283792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        the next/previous track will be picked in sequential order again.
1284792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1285792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1286792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void prev() {
1287792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1288792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mShuffleMode == SHUFFLE_NORMAL) {
1289792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                // go to previously-played track and remove it from the history
1290792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                int histsize = mHistory.size();
1291792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (histsize == 0) {
1292792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    // prev is a no-op
1293792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    return;
1294792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1295792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                Integer pos = mHistory.remove(histsize - 1);
1296792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayPos = pos.intValue();
1297792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } else {
1298792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (mPlayPos > 0) {
1299792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mPlayPos--;
1300792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } else {
1301792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mPlayPos = mPlayListLen - 1;
1302792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1303792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1304792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            saveBookmarkIfNeeded();
1305792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            stop(false);
1306e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            openCurrentAndNext();
1307792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            play();
1308792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            notifyChange(META_CHANGED);
1309792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1310792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1311792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1312bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen    /**
1313bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen     * Get the next position to play. Note that this may actually modify mPlayPos
1314bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen     * if playback is in SHUFFLE_AUTO mode and the shuffle list window needed to
1315bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen     * be adjusted. Either way, the return value is the next value that should be
1316bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen     * assigned to mPlayPos;
1317bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen     */
1318bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen    private int getNextPosition(boolean force) {
1319e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        if (mRepeatMode == REPEAT_CURRENT) {
1320e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            if (mPlayPos < 0) return 0;
1321e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            return mPlayPos;
1322e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        } else if (mShuffleMode == SHUFFLE_NORMAL) {
1323bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            // Pick random next track from the not-yet-played ones
1324bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            // TODO: make it work right after adding/removing items in the queue.
1325663fea34b586fbdf46acebdb86e163aca0e50323Marco Nelissen
1326bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            // Store the current file in the history, but keep the history at a
1327bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            // reasonable size
1328bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            if (mPlayPos >= 0) {
1329bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                mHistory.add(mPlayPos);
1330bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            }
1331bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            if (mHistory.size() > MAX_HISTORY_SIZE) {
1332bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                mHistory.removeElementAt(0);
1333bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            }
1334792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1335bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            int numTracks = mPlayListLen;
1336bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            int[] tracks = new int[numTracks];
1337b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            for (int i = 0; i < numTracks; i++) {
1338bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                tracks[i] = i;
1339bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            }
13403f502de8e8963224054a97730d132f82ac87d522Marco Nelissen
1341bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            int numHistory = mHistory.size();
1342bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            int numUnplayed = numTracks;
1343b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            for (int i = 0; i < numHistory; i++) {
1344bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                int idx = mHistory.get(i).intValue();
1345bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                if (idx < numTracks && tracks[idx] >= 0) {
1346bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    numUnplayed--;
1347bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    tracks[idx] = -1;
1348792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1349bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            }
1350792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1351bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            // 'numUnplayed' now indicates how many tracks have not yet
1352bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            // been played, and 'tracks' contains the indices of those
1353bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            // tracks.
1354b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            if (numUnplayed <= 0) {
1355bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                // everything's already been played
1356bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                if (mRepeatMode == REPEAT_ALL || force) {
1357b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                    // pick from full set
1358bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    numUnplayed = numTracks;
1359b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                    for (int i = 0; i < numTracks; i++) {
1360bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                        tracks[i] = i;
1361792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    }
1362bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                } else {
1363bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    // all done
1364bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    return -1;
1365792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1366bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            }
1367bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            int skip = mRand.nextInt(numUnplayed);
1368bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            int cnt = -1;
1369bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            while (true) {
1370bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                while (tracks[++cnt] < 0)
1371bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    ;
1372bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                skip--;
1373bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                if (skip < 0) {
1374bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    break;
1375792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1376bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            }
1377bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            return cnt;
1378bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen        } else if (mShuffleMode == SHUFFLE_AUTO) {
1379bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            doAutoShuffleUpdate();
1380bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            return mPlayPos + 1;
1381bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen        } else {
1382bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            if (mPlayPos >= mPlayListLen - 1) {
1383bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                // we're at the end of the list
1384bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                if (mRepeatMode == REPEAT_NONE && !force) {
1385bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    // all done
1386bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    return -1;
1387bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                } else if (mRepeatMode == REPEAT_ALL || force) {
1388bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    return 0;
1389792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1390bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                return -1;
1391792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } else {
1392bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                return mPlayPos + 1;
1393bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            }
1394bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen        }
1395bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen    }
1396bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen
1397bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen    public void gotoNext(boolean force) {
1398bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen        synchronized (this) {
1399bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            if (mPlayListLen <= 0) {
1400bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                Log.d(LOGTAG, "No play queue");
1401bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                return;
1402bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            }
1403bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen
1404bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            int pos = getNextPosition(force);
1405bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            if (pos < 0) {
1406bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                gotoIdleState();
1407bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                if (mIsSupposedToBePlaying) {
1408bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    mIsSupposedToBePlaying = false;
1409bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                    notifyChange(PLAYSTATE_CHANGED);
1410792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1411bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen                return;
1412792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1413bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            mPlayPos = pos;
1414792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            saveBookmarkIfNeeded();
1415792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            stop(false);
1416e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mPlayPos = pos;
1417e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            openCurrentAndNext();
1418792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            play();
1419792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            notifyChange(META_CHANGED);
1420792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1421792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1422b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
1423792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private void gotoIdleState() {
1424792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mDelayedStopHandler.removeCallbacksAndMessages(null);
1425792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        Message msg = mDelayedStopHandler.obtainMessage();
1426792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY);
1427d5fc5b6cc39bc2c2693428fe957bb4ae7f90c0f7Dianne Hackborn        stopForeground(true);
1428792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1429b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
1430792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private void saveBookmarkIfNeeded() {
1431792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        try {
1432792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (isPodcast()) {
1433792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                long pos = position();
1434792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                long bookmark = getBookmark();
1435792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                long duration = duration();
1436b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                if ((pos < bookmark && (pos + 10000) > bookmark)
1437b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                        || (pos > bookmark && (pos - 10000) < bookmark)) {
1438792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    // The existing bookmark is close to the current
1439792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    // position, so don't update it.
1440792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    return;
1441792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1442792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (pos < 15000 || (pos + 10000) > duration) {
1443792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    // if we're near the start or end, clear the bookmark
1444792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    pos = 0;
1445792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1446b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
1447792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                // write 'pos' to the bookmark field
1448792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                ContentValues values = new ContentValues();
1449792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                values.put(MediaStore.Audio.Media.BOOKMARK, pos);
1450792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                Uri uri = ContentUris.withAppendedId(
1451792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, mCursor.getLong(IDCOLIDX));
1452792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                getContentResolver().update(uri, values, null, null);
1453792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1454792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        } catch (SQLiteException ex) {
1455792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1456792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1457792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1458792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    // Make sure there are at least 5 items after the currently playing item
1459792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    // and no more than 10 items before.
1460792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private void doAutoShuffleUpdate() {
1461792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        boolean notify = false;
14623f502de8e8963224054a97730d132f82ac87d522Marco Nelissen
1463792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // remove old entries
1464792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mPlayPos > 10) {
1465792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            removeTracks(0, mPlayPos - 9);
1466792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            notify = true;
1467792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1468792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        // add new entries if needed
1469792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        int to_add = 7 - (mPlayListLen - (mPlayPos < 0 ? -1 : mPlayPos));
1470792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        for (int i = 0; i < to_add; i++) {
1471792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            // pick something at random from the list
14723f502de8e8963224054a97730d132f82ac87d522Marco Nelissen
14733f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            int lookback = mHistory.size();
14743f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            int idx = -1;
1475b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            while (true) {
14763f502de8e8963224054a97730d132f82ac87d522Marco Nelissen                idx = mRand.nextInt(mAutoShuffleList.length);
14773f502de8e8963224054a97730d132f82ac87d522Marco Nelissen                if (!wasRecentlyUsed(idx, lookback)) {
14783f502de8e8963224054a97730d132f82ac87d522Marco Nelissen                    break;
14793f502de8e8963224054a97730d132f82ac87d522Marco Nelissen                }
14803f502de8e8963224054a97730d132f82ac87d522Marco Nelissen                lookback /= 2;
14813f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            }
14823f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            mHistory.add(idx);
14833f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            if (mHistory.size() > MAX_HISTORY_SIZE) {
14843f502de8e8963224054a97730d132f82ac87d522Marco Nelissen                mHistory.remove(0);
14853f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            }
1486792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            ensurePlayListCapacity(mPlayListLen + 1);
14873f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            mPlayList[mPlayListLen++] = mAutoShuffleList[idx];
1488792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            notify = true;
1489792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1490792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (notify) {
1491792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            notifyChange(QUEUE_CHANGED);
1492792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1493792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1494792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
14953f502de8e8963224054a97730d132f82ac87d522Marco Nelissen    // check that the specified idx is not in the history (but only look at at
14963f502de8e8963224054a97730d132f82ac87d522Marco Nelissen    // most lookbacksize entries in the history)
14973f502de8e8963224054a97730d132f82ac87d522Marco Nelissen    private boolean wasRecentlyUsed(int idx, int lookbacksize) {
14983f502de8e8963224054a97730d132f82ac87d522Marco Nelissen        // early exit to prevent infinite loops in case idx == mPlayPos
14993f502de8e8963224054a97730d132f82ac87d522Marco Nelissen        if (lookbacksize == 0) {
15003f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            return false;
15013f502de8e8963224054a97730d132f82ac87d522Marco Nelissen        }
15023f502de8e8963224054a97730d132f82ac87d522Marco Nelissen
15033f502de8e8963224054a97730d132f82ac87d522Marco Nelissen        int histsize = mHistory.size();
15043f502de8e8963224054a97730d132f82ac87d522Marco Nelissen        if (histsize < lookbacksize) {
15053f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            Log.d(LOGTAG, "lookback too big");
15063f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            lookbacksize = histsize;
15073f502de8e8963224054a97730d132f82ac87d522Marco Nelissen        }
15083f502de8e8963224054a97730d132f82ac87d522Marco Nelissen        int maxidx = histsize - 1;
15093f502de8e8963224054a97730d132f82ac87d522Marco Nelissen        for (int i = 0; i < lookbacksize; i++) {
15103f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            long entry = mHistory.get(maxidx - i);
15113f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            if (entry == idx) {
15123f502de8e8963224054a97730d132f82ac87d522Marco Nelissen                return true;
15133f502de8e8963224054a97730d132f82ac87d522Marco Nelissen            }
15143f502de8e8963224054a97730d132f82ac87d522Marco Nelissen        }
15153f502de8e8963224054a97730d132f82ac87d522Marco Nelissen        return false;
15163f502de8e8963224054a97730d132f82ac87d522Marco Nelissen    }
15173f502de8e8963224054a97730d132f82ac87d522Marco Nelissen
1518792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    // A simple variation of Random that makes sure that the
1519792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    // value it returns is not equal to the value it returned
1520792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    // previously, unless the interval is 1.
1521756c3f57f11a7bb3bf1cf1333ee8ffbc51039748Marco Nelissen    private static class Shuffler {
1522792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        private int mPrevious;
1523792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        private Random mRandom = new Random();
1524792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public int nextInt(int interval) {
1525792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            int ret;
1526792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            do {
1527792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                ret = mRandom.nextInt(interval);
1528792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } while (ret == mPrevious && interval > 1);
1529792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mPrevious = ret;
1530792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return ret;
1531792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1532792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    };
1533792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1534792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private boolean makeAutoShuffleList() {
1535792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        ContentResolver res = getContentResolver();
1536792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        Cursor c = null;
1537792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        try {
1538792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            c = res.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
1539b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                    new String[] {MediaStore.Audio.Media._ID},
1540b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                    MediaStore.Audio.Media.IS_MUSIC + "=1", null, null);
1541792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (c == null || c.getCount() == 0) {
1542792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return false;
1543792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1544792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            int len = c.getCount();
1545b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            long[] list = new long[len];
1546792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            for (int i = 0; i < len; i++) {
1547792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                c.moveToNext();
1548bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen                list[i] = c.getLong(0);
1549792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1550792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mAutoShuffleList = list;
1551792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return true;
1552792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        } catch (RuntimeException ex) {
1553792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        } finally {
1554792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (c != null) {
1555792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                c.close();
1556792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1557792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1558792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return false;
1559792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1560b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
1561792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1562792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Removes the range of tracks specified from the play list. If a file within the range is
1563792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * the file currently being played, playback will move to the next file after the
1564b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He     * range.
1565792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param first The first file to be removed
1566792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param last The last file to be removed
1567792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @return the number of tracks deleted
1568792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1569792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public int removeTracks(int first, int last) {
1570792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        int numremoved = removeTracksInternal(first, last);
1571792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (numremoved > 0) {
1572792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            notifyChange(QUEUE_CHANGED);
1573792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1574792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return numremoved;
1575792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1576b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
1577792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private int removeTracksInternal(int first, int last) {
1578792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1579792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (last < first) return 0;
1580792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (first < 0) first = 0;
1581792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (last >= mPlayListLen) last = mPlayListLen - 1;
1582792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1583792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            boolean gotonext = false;
1584792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (first <= mPlayPos && mPlayPos <= last) {
1585792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayPos = first;
1586792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                gotonext = true;
1587792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } else if (mPlayPos > last) {
1588792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayPos -= (last - first + 1);
1589792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1590792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            int num = mPlayListLen - last - 1;
1591792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            for (int i = 0; i < num; i++) {
1592792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                mPlayList[first + i] = mPlayList[last + 1 + i];
1593792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1594792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mPlayListLen -= last - first + 1;
1595b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
1596792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (gotonext) {
1597792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (mPlayListLen == 0) {
1598792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    stop(true);
1599792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mPlayPos = -1;
16003aa9ad029bc85da2e07bba019cec928ad5d74043Marco Nelissen                    if (mCursor != null) {
16013aa9ad029bc85da2e07bba019cec928ad5d74043Marco Nelissen                        mCursor.close();
16023aa9ad029bc85da2e07bba019cec928ad5d74043Marco Nelissen                        mCursor = null;
16033aa9ad029bc85da2e07bba019cec928ad5d74043Marco Nelissen                    }
1604792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } else {
1605792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    if (mPlayPos >= mPlayListLen) {
1606792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        mPlayPos = 0;
1607792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    }
1608792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    boolean wasPlaying = isPlaying();
1609792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    stop(false);
1610e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    openCurrentAndNext();
1611792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    if (wasPlaying) {
1612792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                        play();
1613792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    }
1614792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
16153aa9ad029bc85da2e07bba019cec928ad5d74043Marco Nelissen                notifyChange(META_CHANGED);
1616792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1617792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return last - first + 1;
1618792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1619792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1620b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
1621792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1622792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Removes all instances of the track with the given id
1623792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * from the playlist.
1624792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param id The id to be removed
1625792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @return how many instances of the track were removed
1626792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1627bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen    public int removeTrack(long id) {
1628792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        int numremoved = 0;
1629792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1630792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            for (int i = 0; i < mPlayListLen; i++) {
1631792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (mPlayList[i] == id) {
1632792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    numremoved += removeTracksInternal(i, i);
1633792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    i--;
1634792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1635792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1636792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1637792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (numremoved > 0) {
1638792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            notifyChange(QUEUE_CHANGED);
1639792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1640792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return numremoved;
1641792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1642b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
1643792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void setShuffleMode(int shufflemode) {
1644b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        synchronized (this) {
1645792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mShuffleMode == shufflemode && mPlayListLen > 0) {
1646792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return;
1647792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1648792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mShuffleMode = shufflemode;
1649792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mShuffleMode == SHUFFLE_AUTO) {
1650792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (makeAutoShuffleList()) {
1651792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mPlayListLen = 0;
1652792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    doAutoShuffleUpdate();
1653792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mPlayPos = 0;
1654e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    openCurrentAndNext();
1655792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    play();
1656792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    notifyChange(META_CHANGED);
1657792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    return;
1658792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } else {
1659792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    // failed to build a list of files to shuffle
1660792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                    mShuffleMode = SHUFFLE_NONE;
1661792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1662792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1663792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            saveQueue(false);
1664792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1665792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1666792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public int getShuffleMode() {
1667792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return mShuffleMode;
1668792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1669b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
1670792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void setRepeatMode(int repeatmode) {
1671b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        synchronized (this) {
1672792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mRepeatMode = repeatmode;
1673e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            setNextTrack();
1674792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            saveQueue(false);
1675792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1676792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1677792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public int getRepeatMode() {
1678792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return mRepeatMode;
1679792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1680792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1681792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public int getMediaMountedCount() {
1682792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return mMediaMountedCount;
1683792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1684792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1685792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1686792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Returns the path of the currently playing file, or null if
1687792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * no file is currently playing.
1688792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1689792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public String getPath() {
1690792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return mFileToPlay;
1691792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1692b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
1693792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1694792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Returns the rowid of the currently playing file, or -1 if
1695792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * no file is currently playing.
1696792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1697bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen    public long getAudioId() {
1698792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1699792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mPlayPos >= 0 && mPlayer.isInitialized()) {
1700792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return mPlayList[mPlayPos];
1701792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1702792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1703792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return -1;
1704792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1705b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
1706792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1707b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He     * Returns the position in the queue
1708792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @return the position in the queue
1709792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1710792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public int getQueuePosition() {
1711b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        synchronized (this) {
1712792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return mPlayPos;
1713792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1714792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1715b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
1716792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1717792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Starts playing the track at the given position in the queue.
1718792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param pos The position in the queue of the track that will be played.
1719792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1720792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public void setQueuePosition(int pos) {
1721b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        synchronized (this) {
1722792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            stop(false);
1723792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mPlayPos = pos;
1724e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            openCurrentAndNext();
1725792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            play();
1726792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            notifyChange(META_CHANGED);
1727ec0c57a414c3563ebbf5767d92787a6a3f4a8820Marco Nelissen            if (mShuffleMode == SHUFFLE_AUTO) {
1728ec0c57a414c3563ebbf5767d92787a6a3f4a8820Marco Nelissen                doAutoShuffleUpdate();
1729ec0c57a414c3563ebbf5767d92787a6a3f4a8820Marco Nelissen            }
1730792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1731792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1732792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1733792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public String getArtistName() {
1734b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        synchronized (this) {
1735792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mCursor == null) {
1736792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return null;
1737792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1738792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return mCursor.getString(mCursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST));
1739792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1740792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1741b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
1742bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen    public long getArtistId() {
1743792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1744792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mCursor == null) {
1745792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return -1;
1746792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1747bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen            return mCursor.getLong(mCursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST_ID));
1748792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1749792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1750792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1751792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public String getAlbumName() {
1752792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1753792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mCursor == null) {
1754792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return null;
1755792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1756792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return mCursor.getString(mCursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM));
1757792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1758792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1759792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1760bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen    public long getAlbumId() {
1761792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1762792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mCursor == null) {
1763792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return -1;
1764792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1765bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen            return mCursor.getLong(mCursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID));
1766792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1767792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1768792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1769792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public String getTrackName() {
1770792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1771792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mCursor == null) {
1772792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return null;
1773792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1774792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return mCursor.getString(mCursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));
1775792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1776792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1777792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1778792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private boolean isPodcast() {
1779792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1780792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mCursor == null) {
1781792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return false;
1782792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1783792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return (mCursor.getInt(PODCASTCOLIDX) > 0);
1784792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1785792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1786b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
1787792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private long getBookmark() {
1788792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        synchronized (this) {
1789792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (mCursor == null) {
1790792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return 0;
1791792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1792792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return mCursor.getLong(BOOKMARKCOLIDX);
1793792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1794792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1795b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
1796792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1797792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Returns the duration of the file in milliseconds.
1798792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Currently this method returns -1 for the duration of MIDI files.
1799792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1800792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public long duration() {
1801792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mPlayer.isInitialized()) {
1802792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return mPlayer.duration();
1803792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1804792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return -1;
1805792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1806792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1807792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1808792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Returns the current playback position in milliseconds
1809792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1810792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public long position() {
1811792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mPlayer.isInitialized()) {
1812792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return mPlayer.position();
1813792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1814792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return -1;
1815792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1816792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1817792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
1818792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Seeks to the position specified.
1819792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     *
1820792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * @param pos The position to seek to, in milliseconds
1821792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1822792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    public long seek(long pos) {
1823792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        if (mPlayer.isInitialized()) {
1824792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (pos < 0) pos = 0;
1825792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            if (pos > mPlayer.duration()) pos = mPlayer.duration();
1826792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return mPlayer.seek(pos);
1827792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1828792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        return -1;
1829792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
1830792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1831792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    /**
18321cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent     * Sets the audio session ID.
18331cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent     *
18341cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent     * @param sessionId: the audio session ID.
18351cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent     */
18361cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent    public void setAudioSessionId(int sessionId) {
18371cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent        synchronized (this) {
18381cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent            mPlayer.setAudioSessionId(sessionId);
18391cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent        }
18401cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent    }
18411cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent
18421cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent    /**
18431cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent     * Returns the audio session ID.
18441cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent     */
18451cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent    public int getAudioSessionId() {
18461cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent        synchronized (this) {
18471cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent            return mPlayer.getAudioSessionId();
18481cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent        }
18491cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent    }
18501cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent
18511cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent    /**
1852792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * Provides a unified interface for dealing with midi files and
1853792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     * other media files.
1854792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project     */
1855792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    private class MultiPlayer {
1856308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        private CompatMediaPlayer mCurrentMediaPlayer = new CompatMediaPlayer();
1857308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        private CompatMediaPlayer mNextMediaPlayer;
1858792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        private Handler mHandler;
1859792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        private boolean mIsInitialized = false;
1860792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1861792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public MultiPlayer() {
1862b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            mCurrentMediaPlayer.setWakeMode(
1863b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                    MediaPlaybackService.this, PowerManager.PARTIAL_WAKE_LOCK);
1864792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1865792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1866792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void setDataSource(String path) {
186790d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            mIsInitialized = setDataSourceImpl(mCurrentMediaPlayer, path);
186890d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            if (mIsInitialized) {
186990d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                setNextDataSource(null);
187090d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            }
1871e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        }
1872e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen
187390d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen        private boolean setDataSourceImpl(MediaPlayer player, String path) {
1874792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            try {
1875e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                player.reset();
1876e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                player.setOnPreparedListener(null);
1877792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                if (path.startsWith("content://")) {
1878e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    player.setDataSource(MediaPlaybackService.this, Uri.parse(path));
1879792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                } else {
1880e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    player.setDataSource(path);
1881792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1882e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                player.setAudioStreamType(AudioManager.STREAM_MUSIC);
1883e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                player.prepare();
1884792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } catch (IOException ex) {
1885792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                // TODO: notify the user why the file couldn't be opened
188690d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                return false;
1887792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            } catch (IllegalArgumentException ex) {
1888792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                // TODO: notify the user why the file couldn't be opened
188990d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                return false;
1890792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1891e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            player.setOnCompletionListener(listener);
1892e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            player.setOnErrorListener(errorListener);
1893f2ef3b5153fd2f447939f267d8887ff3bba0d13fMarco Nelissen            Intent i = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
1894f2ef3b5153fd2f447939f267d8887ff3bba0d13fMarco Nelissen            i.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, getAudioSessionId());
1895f2ef3b5153fd2f447939f267d8887ff3bba0d13fMarco Nelissen            i.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName());
1896f2ef3b5153fd2f447939f267d8887ff3bba0d13fMarco Nelissen            sendBroadcast(i);
189790d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            return true;
1898792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1899e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen
1900e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        public void setNextDataSource(String path) {
1901e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mCurrentMediaPlayer.setNextMediaPlayer(null);
1902e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            if (mNextMediaPlayer != null) {
1903e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                mNextMediaPlayer.release();
1904e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                mNextMediaPlayer = null;
1905e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            }
1906e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            if (path == null) {
1907e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                return;
1908e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            }
1909308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            mNextMediaPlayer = new CompatMediaPlayer();
1910e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mNextMediaPlayer.setWakeMode(MediaPlaybackService.this, PowerManager.PARTIAL_WAKE_LOCK);
1911e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mNextMediaPlayer.setAudioSessionId(getAudioSessionId());
191290d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            if (setDataSourceImpl(mNextMediaPlayer, path)) {
191390d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                mCurrentMediaPlayer.setNextMediaPlayer(mNextMediaPlayer);
191490d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            } else {
191590d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                // failed to open next, we'll transition the old fashioned way,
191690d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                // which will skip over the faulty file
191790d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                mNextMediaPlayer.release();
191890d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen                mNextMediaPlayer = null;
191990d1f62b8edbc03a0d0b2b42217e9004e7d658d8Marco Nelissen            }
1920e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        }
1921b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
1922792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public boolean isInitialized() {
1923792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return mIsInitialized;
1924792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1925792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1926792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void start() {
19273988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen            MusicUtils.debugLog(new Exception("MultiPlayer.start called"));
1928e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mCurrentMediaPlayer.start();
1929792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1930792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1931792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void stop() {
1932e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mCurrentMediaPlayer.reset();
1933792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mIsInitialized = false;
1934792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1935792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
19362b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        /**
19372b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen         * You CANNOT use this player anymore after calling release()
19382b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen         */
19392b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        public void release() {
19402b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            stop();
1941e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mCurrentMediaPlayer.release();
19422b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        }
1943b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
1944792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void pause() {
1945e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mCurrentMediaPlayer.pause();
1946792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1947b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
1948792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void setHandler(Handler handler) {
1949792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            mHandler = handler;
1950792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1951792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1952792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        MediaPlayer.OnCompletionListener listener = new MediaPlayer.OnCompletionListener() {
1953792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            public void onCompletion(MediaPlayer mp) {
1954e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                if (mp == mCurrentMediaPlayer && mNextMediaPlayer != null) {
1955e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    mCurrentMediaPlayer.release();
1956e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    mCurrentMediaPlayer = mNextMediaPlayer;
1957e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    mNextMediaPlayer = null;
1958e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    mHandler.sendEmptyMessage(TRACK_WENT_TO_NEXT);
1959e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                } else {
1960e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    // Acquire a temporary wakelock, since when we return from
1961e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    // this callback the MediaPlayer will release its wakelock
1962e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    // and allow the device to go to sleep.
1963e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    // This temporary wakelock is released when the RELEASE_WAKELOCK
1964e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    // message is processed, but just in case, put a timeout on it.
1965e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    mWakeLock.acquire(30000);
1966e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    mHandler.sendEmptyMessage(TRACK_ENDED);
1967e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                    mHandler.sendEmptyMessage(RELEASE_WAKELOCK);
1968e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen                }
1969792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            }
1970792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        };
1971792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1972792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        MediaPlayer.OnErrorListener errorListener = new MediaPlayer.OnErrorListener() {
1973792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            public boolean onError(MediaPlayer mp, int what, int extra) {
1974792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                switch (what) {
1975b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                    case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
1976b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                        mIsInitialized = false;
1977b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                        mCurrentMediaPlayer.release();
1978b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                        // Creating a new MediaPlayer and settings its wakemode does not
1979b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                        // require the media service, so it's OK to do this now, while the
1980b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                        // service is still being restarted
1981b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                        mCurrentMediaPlayer = new CompatMediaPlayer();
1982b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                        mCurrentMediaPlayer.setWakeMode(
1983b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                                MediaPlaybackService.this, PowerManager.PARTIAL_WAKE_LOCK);
1984b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                        mHandler.sendMessageDelayed(mHandler.obtainMessage(SERVER_DIED), 2000);
1985b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                        return true;
1986b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                    default:
1987b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                        Log.d("MultiPlayer", "Error: " + what + "," + extra);
1988b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He                        break;
1989792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                }
1990792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project                return false;
1991b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He            }
1992792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        };
1993792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1994792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public long duration() {
1995e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            return mCurrentMediaPlayer.getDuration();
1996792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
1997792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
1998792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public long position() {
1999e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            return mCurrentMediaPlayer.getCurrentPosition();
2000792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2001792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
2002792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public long seek(long whereto) {
2003e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mCurrentMediaPlayer.seekTo((int) whereto);
2004792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project            return whereto;
2005792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2006792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
2007792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void setVolume(float vol) {
2008e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mCurrentMediaPlayer.setVolume(vol, vol);
2009792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
20101cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent
20111cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent        public void setAudioSessionId(int sessionId) {
2012e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            mCurrentMediaPlayer.setAudioSessionId(sessionId);
20131cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent        }
20141cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent
20151cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent        public int getAudioSessionId() {
2016e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen            return mCurrentMediaPlayer.getAudioSessionId();
20171cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent        }
2018792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project    }
2019792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project
2020308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen    static class CompatMediaPlayer extends MediaPlayer implements OnCompletionListener {
2021308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        private boolean mCompatMode = true;
2022308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        private MediaPlayer mNextPlayer;
2023308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        private OnCompletionListener mCompletion;
2024308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen
2025308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        public CompatMediaPlayer() {
2026308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            try {
2027308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                MediaPlayer.class.getMethod("setNextMediaPlayer", MediaPlayer.class);
2028308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                mCompatMode = false;
2029308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            } catch (NoSuchMethodException e) {
2030308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                mCompatMode = true;
2031308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                super.setOnCompletionListener(this);
2032308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            }
2033308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        }
2034308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen
2035308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        public void setNextMediaPlayer(MediaPlayer next) {
2036308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            if (mCompatMode) {
2037308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                mNextPlayer = next;
2038308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            } else {
2039308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                super.setNextMediaPlayer(next);
2040308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            }
2041308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        }
2042308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen
2043308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        @Override
2044308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        public void setOnCompletionListener(OnCompletionListener listener) {
2045308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            if (mCompatMode) {
2046308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                mCompletion = listener;
2047308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            } else {
2048308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                super.setOnCompletionListener(listener);
2049308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            }
2050308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        }
2051308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen
2052308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        @Override
2053308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        public void onCompletion(MediaPlayer mp) {
2054308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            if (mNextPlayer != null) {
2055308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                // as it turns out, starting a new MediaPlayer on the completion
2056308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                // of a previous player ends up slightly overlapping the two
2057308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                // playbacks, so slightly delaying the start of the next player
2058308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                // gives a better user experience
2059308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                SystemClock.sleep(50);
2060308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen                mNextPlayer.start();
2061308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            }
2062308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen            mCompletion.onCompletion(this);
2063308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen        }
2064308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen    }
2065308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen
20662b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen    /*
20672b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen     * By making this a static class with a WeakReference to the Service, we
20682b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen     * ensure that the Service can be GCd even when the system process still
20692b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen     * has a remote reference to the stub.
20702b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen     */
20712b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen    static class ServiceStub extends IMediaPlaybackService.Stub {
20722b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        WeakReference<MediaPlaybackService> mService;
2073b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He
20742b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        ServiceStub(MediaPlaybackService service) {
20752b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService = new WeakReference<MediaPlaybackService>(service);
20762b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen        }
20772b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen
2078b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        public void openFile(String path) {
20798d08ec235831d71fdd7f7b6f7757c2bc19528faeMarco Nelissen            mService.get().open(path);
2080792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2081b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        public void open(long[] list, int position) {
20822b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService.get().open(list, position);
2083792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2084792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public int getQueuePosition() {
20852b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getQueuePosition();
2086792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2087792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void setQueuePosition(int index) {
20882b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService.get().setQueuePosition(index);
2089792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2090792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public boolean isPlaying() {
20912b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().isPlaying();
2092792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2093792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void stop() {
20942b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService.get().stop();
2095792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2096792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void pause() {
20972b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService.get().pause();
2098792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2099792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void play() {
21002b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService.get().play();
2101792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2102792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void prev() {
21032b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService.get().prev();
2104792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2105792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void next() {
2106bd0d2f10e06ec6fb83e21cb4966032916f68acc3Marco Nelissen            mService.get().gotoNext(true);
2107792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2108792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public String getTrackName() {
21092b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getTrackName();
2110792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2111792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public String getAlbumName() {
21122b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getAlbumName();
2113792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2114bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen        public long getAlbumId() {
21152b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getAlbumId();
2116792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2117792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public String getArtistName() {
21182b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getArtistName();
2119792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2120bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen        public long getArtistId() {
21212b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getArtistId();
2122792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2123b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        public void enqueue(long[] list, int action) {
21242b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService.get().enqueue(list, action);
2125792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2126b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He        public long[] getQueue() {
21272b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getQueue();
2128792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2129792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void moveQueueItem(int from, int to) {
21302b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService.get().moveQueueItem(from, to);
2131792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2132792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public String getPath() {
21332b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getPath();
2134792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2135bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen        public long getAudioId() {
21362b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getAudioId();
2137792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2138792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public long position() {
21392b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().position();
2140792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2141792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public long duration() {
21422b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().duration();
2143792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2144792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public long seek(long pos) {
21452b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().seek(pos);
2146792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2147792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void setShuffleMode(int shufflemode) {
21482b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService.get().setShuffleMode(shufflemode);
2149792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2150792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public int getShuffleMode() {
21512b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getShuffleMode();
2152792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2153792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public int removeTracks(int first, int last) {
21542b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().removeTracks(first, last);
2155792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2156bd447b61c42589133257fb7992ccbed18a507a46Marco Nelissen        public int removeTrack(long id) {
21572b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().removeTrack(id);
2158792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2159792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public void setRepeatMode(int repeatmode) {
21602b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            mService.get().setRepeatMode(repeatmode);
2161792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2162792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public int getRepeatMode() {
21632b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getRepeatMode();
2164792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
2165792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        public int getMediaMountedCount() {
21662b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen            return mService.get().getMediaMountedCount();
2167792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project        }
21681cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent        public int getAudioSessionId() {
21691cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent            return mService.get().getAudioSessionId();
21701cc72a178de85e9aa254906dadb54a51423731f3Eric Laurent        }
21712b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen    }
21723988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen
21733988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen    @Override
21743988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
2175bf555cee7a01aed223b05144d1cec0cffdd9a6aeMarco Nelissen        writer.println("" + mPlayListLen + " items in queue, currently at index " + mPlayPos);
21763988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen        writer.println("Currently loaded:");
21773988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen        writer.println(getArtistName());
21783988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen        writer.println(getAlbumName());
21793988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen        writer.println(getTrackName());
21803988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen        writer.println(getPath());
21813988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen        writer.println("playing: " + mIsSupposedToBePlaying);
2182e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen        writer.println("actual: " + mPlayer.mCurrentMediaPlayer.isPlaying());
2183bf555cee7a01aed223b05144d1cec0cffdd9a6aeMarco Nelissen        writer.println("shuffle mode: " + mShuffleMode);
21843988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen        MusicUtils.debugDump(writer);
21853988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen    }
21863988890280c37b2b4aa1cb29ce16da8aa38ffa83Marco Nelissen
21872b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen    private final IBinder mBinder = new ServiceStub(this);
2188792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project}
2189