PageRenderTime 61ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/src/br/com/carlosrafaelgn/fplay/playback/Player.java

https://gitlab.com/madamovic-bg/FPlayAndroid
Java | 2954 lines | 2477 code | 192 blank | 285 comment | 735 complexity | faee75eb9cc0c9a531b46314c416bc5b MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. //
  2. // FPlayAndroid is distributed under the FreeBSD License
  3. //
  4. // Copyright (c) 2013-2014, Carlos Rafael Gimenes das Neves
  5. // All rights reserved.
  6. //
  7. // Redistribution and use in source and binary forms, with or without
  8. // modification, are permitted provided that the following conditions are met:
  9. //
  10. // 1. Redistributions of source code must retain the above copyright notice, this
  11. // list of conditions and the following disclaimer.
  12. // 2. Redistributions in binary form must reproduce the above copyright notice,
  13. // this list of conditions and the following disclaimer in the documentation
  14. // and/or other materials provided with the distribution.
  15. //
  16. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  17. // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  20. // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. //
  27. // The views and conclusions contained in the software and documentation are those
  28. // of the authors and should not be interpreted as representing official policies,
  29. // either expressed or implied, of the FreeBSD Project.
  30. //
  31. // https://github.com/carlosrafaelgn/FPlayAndroid
  32. //
  33. package br.com.carlosrafaelgn.fplay.playback;
  34. import android.Manifest;
  35. import android.annotation.TargetApi;
  36. import android.app.AlarmManager;
  37. import android.app.Notification;
  38. import android.app.NotificationManager;
  39. import android.app.PendingIntent;
  40. import android.app.Service;
  41. import android.content.ComponentName;
  42. import android.content.Context;
  43. import android.content.Intent;
  44. import android.content.IntentFilter;
  45. import android.content.pm.PackageManager;
  46. import android.media.AudioManager;
  47. import android.media.MediaMetadata;
  48. import android.media.MediaMetadataRetriever;
  49. import android.media.MediaPlayer;
  50. import android.media.MediaRouter;
  51. import android.media.RemoteControlClient;
  52. import android.media.session.MediaSession;
  53. import android.media.session.PlaybackState;
  54. import android.net.ConnectivityManager;
  55. import android.net.NetworkInfo;
  56. import android.os.Build;
  57. import android.os.Handler;
  58. import android.os.IBinder;
  59. import android.os.Looper;
  60. import android.os.Message;
  61. import android.os.PowerManager;
  62. import android.os.SystemClock;
  63. import android.support.annotation.NonNull;
  64. import android.telephony.TelephonyManager;
  65. import android.view.KeyEvent;
  66. import android.widget.RemoteViews;
  67. import java.io.FileNotFoundException;
  68. import java.io.IOException;
  69. import java.util.ArrayList;
  70. import java.util.HashSet;
  71. import br.com.carlosrafaelgn.fplay.ExternalReceiver;
  72. import br.com.carlosrafaelgn.fplay.R;
  73. import br.com.carlosrafaelgn.fplay.WidgetMain;
  74. import br.com.carlosrafaelgn.fplay.activity.ActivityHost;
  75. import br.com.carlosrafaelgn.fplay.activity.ClientActivity;
  76. import br.com.carlosrafaelgn.fplay.activity.MainHandler;
  77. import br.com.carlosrafaelgn.fplay.list.FileSt;
  78. import br.com.carlosrafaelgn.fplay.list.Song;
  79. import br.com.carlosrafaelgn.fplay.list.SongList;
  80. import br.com.carlosrafaelgn.fplay.ui.BgListView;
  81. import br.com.carlosrafaelgn.fplay.ui.UI;
  82. import br.com.carlosrafaelgn.fplay.util.ArraySorter;
  83. import br.com.carlosrafaelgn.fplay.util.SerializableMap;
  84. import br.com.carlosrafaelgn.fplay.visualizer.BluetoothVisualizerControllerJni;
  85. //
  86. //MediaPlayer CALLBACKS ARE CALLED ON THE THREAD WHERE THE PLAYER IS CREATED (WHICH
  87. //IS THE MAIN THREAD, IN OUR CASE) THEREFORE, THERE IS NO NEED TO SYNCHRONIZE CALLS
  88. //AS LONG AS previous(), play(), playPause() AND next() ARE ALWAYS CALLED FROM THE
  89. //MAIN THREAD!!!
  90. //http://stackoverflow.com/questions/3919089/do-callbacks-occur-on-the-main-ui-thread
  91. //http://stackoverflow.com/questions/11066186/how-does-android-perform-callbacks-on-my-thread
  92. //
  93. //
  94. //Android Audio: Play an MP3 file on an AudioTrack
  95. //http://mindtherobot.com/blog/624/android-audio-play-an-mp3-file-on-an-audiotrack/
  96. //
  97. //Android Audio: Play a WAV file on an AudioTrack
  98. //http://mindtherobot.com/blog/580/android-audio-play-a-wav-file-on-an-audiotrack/
  99. //
  100. //Equalizer
  101. //http://developer.android.com/reference/android/media/audiofx/Equalizer.html
  102. //
  103. //Media Playback
  104. //http://developer.android.com/guide/topics/media/mediaplayer.html
  105. //
  106. //MediaPlayer (including state diagram)
  107. //http://developer.android.com/reference/android/media/MediaPlayer.html
  108. //
  109. //AudioTrack
  110. //http://developer.android.com/reference/android/media/AudioTrack.html
  111. //
  112. //Allowing applications to play nice(r) with each other: Handling remote control buttons
  113. //http://android-developers.blogspot.com.br/2010/06/allowing-applications-to-play-nicer.html
  114. //
  115. //Many properties used to be private, but I decided to make them public, even though some of them
  116. //still have their setters, after I found out ProGuard does not inline static setters (or at least
  117. //I have not been able to figure out a way to do so....)
  118. //************************************************************************************
  119. //In a few devices, error -38 will happen on the MediaPlayer currently being played
  120. //when adding breakpoints to Player Core Thread
  121. //************************************************************************************
  122. public final class Player extends Service implements AudioManager.OnAudioFocusChangeListener, MediaPlayer.OnErrorListener, MediaPlayer.OnSeekCompleteListener, MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnInfoListener, ArraySorter.Comparer<FileSt> {
  123. public interface PlayerObserver {
  124. void onPlayerChanged(Song currentSong, boolean songHasChanged, boolean preparingHasChanged, Throwable ex);
  125. void onPlayerControlModeChanged(boolean controlMode);
  126. void onPlayerGlobalVolumeChanged(int volume);
  127. void onPlayerAudioSinkChanged(int audioSink);
  128. void onPlayerMediaButtonPrevious();
  129. void onPlayerMediaButtonNext();
  130. }
  131. public interface PlayerTurnOffTimerObserver {
  132. void onPlayerTurnOffTimerTick();
  133. void onPlayerIdleTurnOffTimerTick();
  134. }
  135. public interface PlayerDestroyedObserver {
  136. void onPlayerDestroyed();
  137. }
  138. private static final class TimeoutException extends Exception {
  139. private static final long serialVersionUID = 4571328670214281144L;
  140. }
  141. private static final class MediaServerDiedException extends Exception {
  142. private static final long serialVersionUID = -902099312236606175L;
  143. }
  144. private static final class FocusException extends Exception {
  145. private static final long serialVersionUID = 6158088015763157546L;
  146. }
  147. private static final class PermissionDeniedException extends SecurityException {
  148. private static final long serialVersionUID = 8743650824658438278L;
  149. }
  150. private static final int MSG_UPDATE_STATE = 0x0100;
  151. private static final int MSG_PAUSE = 0x0101;
  152. private static final int MSG_PLAYPAUSE = 0x0102;
  153. private static final int MSG_SEEKTO = 0x0103;
  154. private static final int MSG_SYNC_VOLUME = 0x0104;
  155. private static final int MSG_PREPARE_NEXT_SONG = 0x0105;
  156. private static final int MSG_OVERRIDE_VOLUME_MULTIPLIER = 0x0106;
  157. private static final int MSG_BECOMING_NOISY = 0x0107;
  158. private static final int MSG_AUDIO_SINK_CHANGED = 0x0108;
  159. private static final int MSG_INITIALIZATION_STEP = 0x0109;
  160. private static final int MSG_NEXT_MAY_HAVE_CHANGED = 0x010A;
  161. private static final int MSG_LIST_CLEARED = 0x010B;
  162. private static final int MSG_FOCUS_GAIN_TIMER = 0x010C;
  163. private static final int MSG_FADE_IN_VOLUME_TIMER = 0x010D;
  164. private static final int MSG_HEADSET_HOOK_TIMER = 0x010E;
  165. private static final int MSG_TURN_OFF_TIMER = 0x010F;
  166. private static final int MSG_IDLE_TURN_OFF_TIMER = 0x0110;
  167. private static final int MSG_REGISTER_MEDIA_BUTTON_EVENT_RECEIVER = 0x0111;
  168. private static final int MSG_SONG_LIST_DESERIALIZED = 0x0112;
  169. private static final int MSG_PRE_PLAY = 0x0113;
  170. private static final int MSG_POST_PLAY = 0x0114;
  171. private static final int MSG_ENABLE_EFFECTS = 0x0115;
  172. private static final int MSG_COMMIT_EQUALIZER = 0x0116;
  173. private static final int MSG_COMMIT_BASS_BOOST = 0x0117;
  174. private static final int MSG_COMMIT_VIRTUALIZER = 0x0118;
  175. private static final int MSG_TURN_OFF_NOW = 0x0119;
  176. public static final int STATE_NEW = 0;
  177. public static final int STATE_INITIALIZING = 1;
  178. public static final int STATE_INITIALIZING_STEP2 = 2;
  179. public static final int STATE_ALIVE = 3;
  180. public static final int STATE_TERMINATING = 4;
  181. public static final int STATE_DEAD = 5;
  182. public static final int PLAYER_STATE_NEW = 0;
  183. public static final int PLAYER_STATE_PREPARING = 1;
  184. public static final int PLAYER_STATE_LOADED = 2;
  185. public static final int AUDIO_SINK_DEVICE = 1;
  186. public static final int AUDIO_SINK_WIRE = 2;
  187. public static final int AUDIO_SINK_BT = 4;
  188. public static final int VOLUME_MIN_DB = -4000;
  189. public static final int VOLUME_CONTROL_DB = 0;
  190. public static final int VOLUME_CONTROL_STREAM = 1;
  191. public static final int VOLUME_CONTROL_NONE = 2;
  192. public static final int VOLUME_CONTROL_PERCENT = 3;
  193. private static final int SILENCE_NORMAL = 0;
  194. private static final int SILENCE_FOCUS = 1;
  195. private static final int SILENCE_NONE = -1;
  196. public static final String ACTION_PREVIOUS = "br.com.carlosrafaelgn.FPlay.PREVIOUS";
  197. public static final String ACTION_PLAY_PAUSE = "br.com.carlosrafaelgn.FPlay.PLAY_PAUSE";
  198. public static final String ACTION_NEXT = "br.com.carlosrafaelgn.FPlay.NEXT";
  199. public static final String ACTION_EXIT = "br.com.carlosrafaelgn.FPlay.EXIT";
  200. public static String pathToPlayWhenStarting;
  201. private static String startCommand;
  202. public static int state;
  203. private static Thread thread;
  204. private static Looper looper;
  205. private static Player thePlayer;
  206. private static Handler handler, localHandler;
  207. private static AudioManager audioManager;
  208. private static NotificationManager notificationManager;
  209. private static TelephonyManager telephonyManager;
  210. public static final SongList songs = SongList.getInstance();
  211. //keep these instances here to prevent UI, MainHandler, Equalizer, BassBoost,
  212. //and Virtualizer classes from being garbage collected...
  213. public static MainHandler theMainHandler;
  214. public static final UI theUI = new UI();
  215. @SuppressWarnings("unused")
  216. public static final Equalizer theEqualizer = new Equalizer();
  217. @SuppressWarnings("unused")
  218. public static final BassBoost theBassBoost = new BassBoost();
  219. @SuppressWarnings("unused")
  220. public static final Virtualizer theVirtualizer = new Virtualizer();
  221. public static boolean hasFocus;
  222. public static int volumeStreamMax = 15, volumeControlType;
  223. private static boolean volumeDimmed;
  224. private static int volumeDB, volumeDBFading, silenceMode;
  225. private static float volumeMultiplier;
  226. private static int audioSink, audioSinkBeforeFocusLoss;
  227. private static int storedSongTime, howThePlayerStarted, playerState, nextPlayerState;
  228. private static boolean resumePlaybackAfterFocusGain, postPlayPending, playing, playerBuffering, playAfterSeeking, prepareNextAfterSeeking, nextAlreadySetForPlaying, reviveAlreadyTried;
  229. private static Song song, nextSong, songScheduledForPreparation, nextSongScheduledForPreparation, songWhenFirstErrorHappened;
  230. private static MediaPlayer player, nextPlayer;
  231. //keep these three fields here, instead of in ActivityMain/ActivityBrowser,
  232. //so they will survive their respective activity's destruction
  233. //(and even the class garbage collection)
  234. public static int lastCurrent = -1, listFirst = -1, listTop = 0, positionToSelect = -1;
  235. public static boolean isMainActiveOnTop, alreadySelected;
  236. //These are only set in the main thread
  237. private static int localVolumeStream, localPlayerState;
  238. public static int localVolumeDB;
  239. public static boolean localPlaying;
  240. public static Song localSong;
  241. public static MediaPlayer localPlayer;
  242. private static class CoreHandler extends Handler {
  243. @Override
  244. public void dispatchMessage(@NonNull Message msg) {
  245. switch (msg.what) {
  246. case MSG_POST_PLAY:
  247. _postPlay(msg.arg1, (Song[])msg.obj);
  248. break;
  249. case MSG_BECOMING_NOISY:
  250. if (playing)
  251. _playPause();
  252. //this cleanup must be done, as sometimes, when changing between two output types,
  253. //the effects are lost...
  254. _fullCleanup();
  255. _checkAudioSink(false, false);
  256. break;
  257. case MSG_FADE_IN_VOLUME_TIMER:
  258. _processFadeInVolumeTimer(msg.arg1);
  259. break;
  260. case MSG_AUDIO_SINK_CHANGED:
  261. _checkAudioSink(msg.arg1 != 0, true);
  262. break;
  263. case MSG_PAUSE:
  264. if (playing)
  265. _playPause();
  266. break;
  267. case MSG_PLAYPAUSE:
  268. _playPause();
  269. break;
  270. case MSG_SEEKTO:
  271. _seekTo(msg.arg1);
  272. break;
  273. case MSG_SYNC_VOLUME:
  274. _syncVolume();
  275. break;
  276. case MSG_PREPARE_NEXT_SONG:
  277. _prepareNextPlayer((Song)msg.obj);
  278. break;
  279. case MSG_FOCUS_GAIN_TIMER:
  280. _processFocusGainTimer();
  281. break;
  282. case MSG_OVERRIDE_VOLUME_MULTIPLIER:
  283. _overrideVolumeMultiplier(msg.arg1 != 0);
  284. break;
  285. case MSG_LIST_CLEARED:
  286. _fullCleanup();
  287. song = null;
  288. storedSongTime = -1;
  289. _updateState(false, null);
  290. break;
  291. case MSG_NEXT_MAY_HAVE_CHANGED:
  292. _nextMayHaveChanged((Song)msg.obj);
  293. break;
  294. case MSG_ENABLE_EFFECTS:
  295. _enableEffects(msg.arg1, (Runnable)msg.obj);
  296. break;
  297. case MSG_COMMIT_EQUALIZER:
  298. Equalizer.commit(msg.arg1);
  299. break;
  300. case MSG_COMMIT_BASS_BOOST:
  301. BassBoost.commit();
  302. break;
  303. case MSG_COMMIT_VIRTUALIZER:
  304. Virtualizer.commit();
  305. break;
  306. case MSG_SONG_LIST_DESERIALIZED:
  307. _songListDeserialized(msg.obj);
  308. break;
  309. }
  310. }
  311. }
  312. private static class CoreLocalHandler extends Handler {
  313. @Override
  314. public void dispatchMessage(@NonNull Message msg) {
  315. switch (msg.what) {
  316. case MSG_PRE_PLAY:
  317. prePlay(msg.arg1);
  318. break;
  319. case MSG_AUDIO_SINK_CHANGED:
  320. if (observer != null)
  321. observer.onPlayerAudioSinkChanged(audioSink);
  322. break;
  323. case MSG_UPDATE_STATE:
  324. updateState(msg.arg1, msg.arg2, (Song)msg.obj);
  325. break;
  326. case MSG_REGISTER_MEDIA_BUTTON_EVENT_RECEIVER:
  327. registerMediaButtonEventReceiver();
  328. break;
  329. case MSG_INITIALIZATION_STEP:
  330. switch (state) {
  331. case STATE_INITIALIZING:
  332. state = STATE_INITIALIZING_STEP2;
  333. break;
  334. case STATE_INITIALIZING_STEP2:
  335. state = STATE_ALIVE;
  336. handler.sendMessageAtTime(Message.obtain(handler, MSG_OVERRIDE_VOLUME_MULTIPLIER, (volumeControlType != VOLUME_CONTROL_DB && volumeControlType != VOLUME_CONTROL_PERCENT) ? 1 : 0, 0), SystemClock.uptimeMillis());
  337. setTurnOffTimer(turnOffTimerSelectedMinutes);
  338. setIdleTurnOffTimer(idleTurnOffTimerSelectedMinutes);
  339. executeStartCommand();
  340. break;
  341. }
  342. break;
  343. case MSG_HEADSET_HOOK_TIMER:
  344. processHeadsetHookTimer();
  345. break;
  346. case MSG_TURN_OFF_TIMER:
  347. processTurnOffTimer();
  348. break;
  349. case MSG_IDLE_TURN_OFF_TIMER:
  350. idleTurnOffTimerSent = false;
  351. processIdleTurnOffTimer();
  352. break;
  353. case MSG_TURN_OFF_NOW:
  354. stopService(false);
  355. break;
  356. }
  357. }
  358. }
  359. @Override
  360. public void onCreate() {
  361. thePlayer = this;
  362. startService(this);
  363. startForeground(1, getNotification());
  364. super.onCreate();
  365. }
  366. @Override
  367. public void onDestroy() {
  368. super.onDestroy();
  369. System.exit(0);
  370. }
  371. private static void executeStartCommand() {
  372. if (state == STATE_ALIVE) {
  373. if (pathToPlayWhenStarting != null) {
  374. if (songs.addPathAndForceScrollIntoView(pathToPlayWhenStarting, true))
  375. startCommand = null;
  376. pathToPlayWhenStarting = null;
  377. }
  378. if (startCommand != null) {
  379. switch (startCommand) {
  380. case ACTION_PREVIOUS:
  381. previous();
  382. break;
  383. case ACTION_PLAY_PAUSE:
  384. playPause();
  385. break;
  386. case ACTION_NEXT:
  387. next();
  388. break;
  389. case ACTION_EXIT:
  390. stopService(false);
  391. break;
  392. }
  393. startCommand = null;
  394. }
  395. }
  396. }
  397. @Override
  398. public int onStartCommand(Intent intent, int flags, int startId) {
  399. if (intent != null) {
  400. final String command = intent.getAction();
  401. if (command != null) {
  402. startCommand = command;
  403. executeStartCommand();
  404. }
  405. }
  406. return START_STICKY;
  407. }
  408. @Override
  409. public IBinder onBind(Intent intent) {
  410. return null;
  411. }
  412. public static Service getService() {
  413. return thePlayer;
  414. }
  415. public static boolean startService(Context context) {
  416. final boolean stateNew = (state == STATE_NEW);
  417. if (stateNew) {
  418. MainHandler.initialize();
  419. alreadySelected = true; //fix the initial selection when the app is started from the widget
  420. state = STATE_INITIALIZING;
  421. context.startService(new Intent(context, Player.class));
  422. theMainHandler = MainHandler.initialize();
  423. localHandler = new CoreLocalHandler();
  424. notificationManager = (NotificationManager)context.getSystemService(NOTIFICATION_SERVICE);
  425. audioManager = (AudioManager)context.getSystemService(AUDIO_SERVICE);
  426. telephonyManager = (TelephonyManager)context.getSystemService(TELEPHONY_SERVICE);
  427. destroyedObservers = new ArrayList<>(4);
  428. stickyBroadcast = new Intent();
  429. loadConfig(context);
  430. }
  431. //when the player starts from the widget, startService is not called twice, it is
  432. //called only once, from within onCreate, when thePlayer is already != null
  433. if (thePlayer != null && thread == null) {
  434. createIntents(context);
  435. //These broadcast actions are registered here, instead of in the manifest file,
  436. //because a few tests showed that some devices will produce the notifications
  437. //faster this way, specially AUDIO_BECOMING_NOISY. Moreover, by registering here,
  438. //only one BroadcastReceiver is instantiated and used throughout the entire
  439. //application's lifecycle, whereas when the manifest is used, a different instance
  440. //is created to handle every notification! :)
  441. //The only exception is the MEDIA_BUTTON broadcast action, which MUST BE declared
  442. //in the application manifest according to the documentation of the method
  443. //registerMediaButtonEventReceiver!!! :(
  444. final IntentFilter filter = new IntentFilter("android.media.AUDIO_BECOMING_NOISY");
  445. //filter.addAction("android.intent.action.MEDIA_BUTTON");
  446. filter.addAction("android.intent.action.CALL_BUTTON");
  447. filter.addAction("android.intent.action.HEADSET_PLUG");
  448. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
  449. filter.addAction("android.media.ACTION_SCO_AUDIO_STATE_UPDATED");
  450. else
  451. filter.addAction("android.media.SCO_AUDIO_STATE_CHANGED");
  452. filter.addAction("android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED");
  453. filter.addAction("android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED");
  454. //HEADSET_STATE_CHANGED is based on: https://groups.google.com/forum/#!topic/android-developers/pN2k5_kFo4M
  455. filter.addAction("android.bluetooth.intent.action.HEADSET_STATE_CHANGED");
  456. externalReceiver = new ExternalReceiver();
  457. thePlayer.getApplicationContext().registerReceiver(externalReceiver, filter);
  458. registerMediaButtonEventReceiver();
  459. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
  460. registerMediaRouter(thePlayer);
  461. thread = new Thread("Player Core Thread") {
  462. @Override
  463. public void run() {
  464. Looper.prepare();
  465. looper = Looper.myLooper();
  466. handler = new CoreHandler();
  467. _initializePlayers();
  468. Equalizer.initialize(player.getAudioSessionId());
  469. Equalizer.release();
  470. BassBoost.initialize(player.getAudioSessionId());
  471. BassBoost.release();
  472. Virtualizer.initialize(player.getAudioSessionId());
  473. Virtualizer.release();
  474. if (Equalizer.isEnabled()) {
  475. Equalizer.initialize(player.getAudioSessionId());
  476. Equalizer.setEnabled(true);
  477. }
  478. if (BassBoost.isEnabled()) {
  479. BassBoost.initialize(player.getAudioSessionId());
  480. BassBoost.setEnabled(true);
  481. }
  482. if (Virtualizer.isEnabled()) {
  483. Virtualizer.initialize(player.getAudioSessionId());
  484. Virtualizer.setEnabled(true);
  485. }
  486. _checkAudioSink(false, false);
  487. localHandler.sendEmptyMessageAtTime(MSG_INITIALIZATION_STEP, SystemClock.uptimeMillis());
  488. Looper.loop();
  489. if (song != null) {
  490. _storeSongTime();
  491. song = null;
  492. } else {
  493. storedSongTime = -1;
  494. }
  495. _fullCleanup();
  496. hasFocus = false;
  497. if (audioManager != null && thePlayer != null)
  498. audioManager.abandonAudioFocus(thePlayer);
  499. }
  500. };
  501. thread.start();
  502. while (handler == null)
  503. Thread.yield();
  504. songs.startDeserializing(thePlayer, null, true, false, false);
  505. }
  506. //fix the initial selection when the app is started from the widget
  507. alreadySelected = false;
  508. positionToSelect = songs.getCurrentPosition();
  509. return stateNew;
  510. }
  511. public static void stopService(boolean restart) {
  512. if (state != STATE_ALIVE)
  513. return;
  514. state = STATE_TERMINATING;
  515. looper.quit();
  516. try {
  517. thread.join();
  518. } catch (Throwable ex) {
  519. ex.printStackTrace();
  520. }
  521. if (bluetoothVisualizerController != null)
  522. stopBluetoothVisualizer();
  523. unregisterMediaButtonEventReceiver();
  524. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && thePlayer != null)
  525. unregisterMediaRouter(thePlayer);
  526. if (destroyedObservers != null) {
  527. for (int i = destroyedObservers.size() - 1; i >= 0; i--)
  528. destroyedObservers.get(i).onPlayerDestroyed();
  529. destroyedObservers.clear();
  530. destroyedObservers = null;
  531. }
  532. if (thePlayer != null) {
  533. if (externalReceiver != null)
  534. thePlayer.getApplicationContext().unregisterReceiver(externalReceiver);
  535. saveConfig(thePlayer, true);
  536. }
  537. for (int i = statePlayer.length - 1; i >= 0; i--) {
  538. statePlayer[i] = null;
  539. stateEx[i] = null;
  540. }
  541. updateState(~0x27, 0, null);
  542. if (restart && intentActivityHost != null) {
  543. //http://stackoverflow.com/questions/6609414/howto-programatically-restart-android-app
  544. ((AlarmManager)thePlayer.getSystemService(ALARM_SERVICE)).set(AlarmManager.RTC, System.currentTimeMillis() + 300, intentActivityHost);
  545. }
  546. thread = null;
  547. observer = null;
  548. turnOffTimerObserver = null;
  549. theMainHandler = null;
  550. looper = null;
  551. handler = null;
  552. localHandler = null;
  553. notification = null;
  554. notificationRemoteViews = null;
  555. notificationManager = null;
  556. audioManager = null;
  557. telephonyManager = null;
  558. externalReceiver = null;
  559. stickyBroadcast = null;
  560. intentActivityHost = null;
  561. intentPrevious = null;
  562. intentPlayPause = null;
  563. intentNext = null;
  564. intentExit = null;
  565. favoriteFolders.clear();
  566. localSong = null;
  567. localPlayer = null;
  568. stateLastSong = null;
  569. state = STATE_DEAD;
  570. if (thePlayer != null) {
  571. thePlayer.stopForeground(true);
  572. thePlayer.stopSelf();
  573. thePlayer = null;
  574. } else {
  575. System.exit(0);
  576. }
  577. }
  578. private static void prePlay(int how) {
  579. if (state != STATE_ALIVE || postPlayPending)
  580. return;
  581. localSong = songs.getSongAndSetCurrent(how);
  582. final Song[] songArray = new Song[] { localSong, songs.possibleNextSong };
  583. songs.possibleNextSong = null;
  584. postPlayPending = true;
  585. handler.sendMessageAtTime(Message.obtain(handler, MSG_POST_PLAY, how, 0, songArray), SystemClock.uptimeMillis());
  586. }
  587. public static void previous() {
  588. prePlay(SongList.HOW_PREVIOUS);
  589. }
  590. public static void play(int index) {
  591. prePlay(index);
  592. }
  593. public static void pause() {
  594. if (state != STATE_ALIVE)
  595. return;
  596. handler.sendMessageAtTime(Message.obtain(handler, MSG_PAUSE), SystemClock.uptimeMillis());
  597. }
  598. public static void playPause() {
  599. if (state != STATE_ALIVE)
  600. return;
  601. handler.sendMessageAtTime(Message.obtain(handler, MSG_PLAYPAUSE), SystemClock.uptimeMillis());
  602. }
  603. public static void next() {
  604. prePlay(SongList.HOW_NEXT_MANUAL);
  605. }
  606. public static void seekTo(int timeMS) {
  607. if (state != STATE_ALIVE)
  608. return;
  609. handler.sendMessageAtTime(Message.obtain(handler, MSG_SEEKTO, timeMS, 0), SystemClock.uptimeMillis());
  610. }
  611. public static void showStreamVolumeUI() {
  612. try {
  613. if (audioManager != null)
  614. audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_SAME, AudioManager.FLAG_SHOW_UI);
  615. } catch (Throwable ex) {
  616. ex.printStackTrace();
  617. }
  618. }
  619. public static int setVolume(int volume) {
  620. if (state != STATE_ALIVE)
  621. return Integer.MIN_VALUE;
  622. switch (volumeControlType) {
  623. case VOLUME_CONTROL_STREAM:
  624. if (volume < 0)
  625. volume = 0;
  626. else if (volume > volumeStreamMax)
  627. volume = volumeStreamMax;
  628. localVolumeStream = volume;
  629. //apparently a few devices don't like to have the streamVolume changed from another thread....
  630. //maybe there is another reason for why it fails... I just haven't found yet :(
  631. try {
  632. if (audioManager != null)
  633. audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
  634. } catch (Throwable ex) {
  635. ex.printStackTrace();
  636. }
  637. return volume;
  638. case VOLUME_CONTROL_DB:
  639. case VOLUME_CONTROL_PERCENT:
  640. if (volume <= VOLUME_MIN_DB)
  641. volume = VOLUME_MIN_DB;
  642. else if (volume > 0)
  643. volume = 0;
  644. if (localVolumeDB == volume)
  645. return Integer.MIN_VALUE;
  646. localVolumeDB = volume;
  647. break;
  648. default:
  649. showStreamVolumeUI();
  650. return Integer.MIN_VALUE;
  651. }
  652. handler.sendEmptyMessageAtTime(MSG_SYNC_VOLUME, SystemClock.uptimeMillis());
  653. return volume;
  654. }
  655. public static int decreaseVolume() {
  656. switch (volumeControlType) {
  657. case VOLUME_CONTROL_STREAM:
  658. return setVolume(audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) - 1);
  659. case VOLUME_CONTROL_DB:
  660. case VOLUME_CONTROL_PERCENT:
  661. //http://stackoverflow.com/a/4413073/3569421
  662. //(a/b)*b+(a%b) is equal to a
  663. //-350 % 200 = -150
  664. final int leftover = (Player.volumeDB % 200);
  665. return setVolume(Player.volumeDB + ((leftover == 0) ? -200 : (-200 - leftover)));
  666. }
  667. showStreamVolumeUI();
  668. return Integer.MIN_VALUE;
  669. }
  670. public static int increaseVolume() {
  671. switch (volumeControlType) {
  672. case VOLUME_CONTROL_STREAM:
  673. return setVolume(audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) + 1);
  674. case VOLUME_CONTROL_DB:
  675. case VOLUME_CONTROL_PERCENT:
  676. //http://stackoverflow.com/a/4413073/3569421
  677. //(a/b)*b+(a%b) is equal to a
  678. //-350 % 200 = -150
  679. final int leftover = (Player.volumeDB % 200);
  680. return setVolume(Player.volumeDB + ((leftover == 0) ? 200 : -leftover));
  681. }
  682. showStreamVolumeUI();
  683. return Integer.MIN_VALUE;
  684. }
  685. public static int getSystemStreamVolume() {
  686. try {
  687. if (audioManager != null)
  688. return audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
  689. } catch (Throwable ex) {
  690. ex.printStackTrace();
  691. }
  692. return 0;
  693. }
  694. public static void setVolumeInPercentage(int percentage) {
  695. switch (volumeControlType) {
  696. case VOLUME_CONTROL_STREAM:
  697. setVolume((percentage * volumeStreamMax) / 100);
  698. break;
  699. case VOLUME_CONTROL_DB:
  700. case VOLUME_CONTROL_PERCENT:
  701. setVolume((percentage >= 100) ? 0 : ((percentage <= 0) ? VOLUME_MIN_DB : (((100 - percentage) * VOLUME_MIN_DB) / 100)));
  702. break;
  703. }
  704. }
  705. public static int getVolumeInPercentage() {
  706. switch (volumeControlType) {
  707. case VOLUME_CONTROL_STREAM:
  708. final int vol = getSystemStreamVolume();
  709. return ((vol <= 0) ? 0 : ((vol >= volumeStreamMax) ? 100 : ((vol * 100) / volumeStreamMax)));
  710. case VOLUME_CONTROL_DB:
  711. case VOLUME_CONTROL_PERCENT:
  712. return ((localVolumeDB >= 0) ? 100 : ((localVolumeDB <= VOLUME_MIN_DB) ? 0 : (((VOLUME_MIN_DB - localVolumeDB) * 100) / VOLUME_MIN_DB)));
  713. }
  714. return 0;
  715. }
  716. public static void setVolumeControlType(int volumeControlType) {
  717. switch (volumeControlType) {
  718. case VOLUME_CONTROL_DB:
  719. case VOLUME_CONTROL_PERCENT:
  720. case VOLUME_CONTROL_NONE:
  721. Player.volumeControlType = volumeControlType;
  722. if (handler != null)
  723. handler.sendMessageAtTime(Message.obtain(handler, MSG_OVERRIDE_VOLUME_MULTIPLIER, (volumeControlType == VOLUME_CONTROL_NONE) ? 1 : 0, 0), SystemClock.uptimeMillis());
  724. break;
  725. default:
  726. try {
  727. if (audioManager != null)
  728. volumeStreamMax = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
  729. if (volumeStreamMax < 1)
  730. volumeStreamMax = 1;
  731. if (handler != null)
  732. handler.sendMessageAtTime(Message.obtain(handler, MSG_OVERRIDE_VOLUME_MULTIPLIER, 1, 0), SystemClock.uptimeMillis());
  733. Player.volumeControlType = VOLUME_CONTROL_STREAM;
  734. } catch (Throwable ex) {
  735. Player.volumeControlType = VOLUME_CONTROL_NONE;
  736. ex.printStackTrace();
  737. }
  738. break;
  739. }
  740. }
  741. public static void enableEffects(boolean equalizer, boolean bassBoost, boolean virtualizer, Runnable callback) {
  742. if (state != STATE_ALIVE)
  743. return;
  744. handler.sendMessageAtTime(Message.obtain(handler, MSG_ENABLE_EFFECTS, (equalizer ? 1 : 0) | (bassBoost ? 2 : 0) | (virtualizer ? 4 : 0), 0, callback), SystemClock.uptimeMillis());
  745. }
  746. public static void commitEqualizer(int band) {
  747. if (state != STATE_ALIVE)
  748. return;
  749. handler.sendMessageAtTime(Message.obtain(handler, MSG_COMMIT_EQUALIZER, band, 0), SystemClock.uptimeMillis());
  750. }
  751. public static void commitBassBoost() {
  752. if (state != STATE_ALIVE)
  753. return;
  754. handler.sendEmptyMessageAtTime(MSG_COMMIT_BASS_BOOST, SystemClock.uptimeMillis());
  755. }
  756. public static void commitVirtualizer() {
  757. if (state != STATE_ALIVE)
  758. return;
  759. handler.sendEmptyMessageAtTime(MSG_COMMIT_VIRTUALIZER, SystemClock.uptimeMillis());
  760. }
  761. public static boolean isPreparing() {
  762. return (localPlayerState == PLAYER_STATE_PREPARING || playerBuffering);
  763. }
  764. public static int getPosition() {
  765. return (((localPlayer != null) && playerState == PLAYER_STATE_LOADED) ? localPlayer.getCurrentPosition() : ((localSong == null) ? -1 : storedSongTime));
  766. }
  767. public static int getAudioSessionId() {
  768. return ((localPlayer == null) ? -1 : localPlayer.getAudioSessionId());
  769. }
  770. private static MediaPlayer _createPlayer() {
  771. MediaPlayer mp = new MediaPlayer();
  772. mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
  773. mp.setOnErrorListener(thePlayer);
  774. mp.setOnPreparedListener(thePlayer);
  775. mp.setOnSeekCompleteListener(thePlayer);
  776. mp.setOnCompletionListener(thePlayer);
  777. mp.setOnInfoListener(thePlayer);
  778. mp.setWakeMode(thePlayer, PowerManager.PARTIAL_WAKE_LOCK);
  779. return mp;
  780. }
  781. private static void _startPlayer() {
  782. playAfterSeeking = false;
  783. playing = true;
  784. //when dimmed, decreased the volume by 20dB
  785. float multiplier = (volumeDimmed ? (volumeMultiplier * 0.1f) : volumeMultiplier);
  786. if (silenceMode != SILENCE_NONE) {
  787. final int increment = ((silenceMode == SILENCE_FOCUS) ? fadeInIncrementOnFocus : ((howThePlayerStarted == SongList.HOW_CURRENT) ? fadeInIncrementOnPause : fadeInIncrementOnOther));
  788. if (increment > 30) {
  789. volumeDBFading = VOLUME_MIN_DB;
  790. multiplier = 0;
  791. handler.sendMessageAtTime(Message.obtain(handler, MSG_FADE_IN_VOLUME_TIMER, increment, 0), SystemClock.uptimeMillis() + 50);
  792. }
  793. silenceMode = SILENCE_NONE;
  794. }
  795. if (player != null) {
  796. player.setVolume(multiplier, multiplier);
  797. player.start();
  798. }
  799. reviveAlreadyTried = false;
  800. }
  801. private static void _releasePlayer(MediaPlayer mediaPlayer) {
  802. mediaPlayer.setOnErrorListener(null);
  803. mediaPlayer.setOnPreparedListener(null);
  804. mediaPlayer.setOnSeekCompleteListener(null);
  805. mediaPlayer.setOnCompletionListener(null);
  806. mediaPlayer.setOnInfoListener(null);
  807. mediaPlayer.reset();
  808. mediaPlayer.release();
  809. }
  810. @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
  811. private static void _clearNextPlayer() {
  812. //there is no need to call setNextMediaPlayer(null) after calling reset()
  813. try {
  814. player.setNextMediaPlayer(null);
  815. } catch (Throwable ex) {
  816. ex.printStackTrace();
  817. }
  818. }
  819. @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
  820. private static void _setNextPlayer() {
  821. try {
  822. if (player != null) {
  823. player.setNextMediaPlayer(nextPlayer);
  824. nextAlreadySetForPlaying = true;
  825. }
  826. } catch (Throwable ex) {
  827. ex.printStackTrace();
  828. }
  829. }
  830. private static void _partialCleanup() {
  831. playerState = PLAYER_STATE_NEW;
  832. playerBuffering = false;
  833. if (player != null) {
  834. _releasePlayer(player);
  835. player = null;
  836. }
  837. nextAlreadySetForPlaying = false;
  838. nextPlayerState = PLAYER_STATE_NEW;
  839. if (nextPlayer != null) {
  840. _releasePlayer(nextPlayer);
  841. nextPlayer = null;
  842. }
  843. playing = false;
  844. playAfterSeeking = false;
  845. prepareNextAfterSeeking = false;
  846. resumePlaybackAfterFocusGain = false;
  847. songScheduledForPreparation = null;
  848. nextSongScheduledForPreparation = null;
  849. if (handler != null) {
  850. handler.removeMessages(MSG_FOCUS_GAIN_TIMER);
  851. handler.removeMessages(MSG_FADE_IN_VOLUME_TIMER);
  852. }
  853. if (localHandler != null)
  854. localHandler.removeMessages(MSG_HEADSET_HOOK_TIMER);
  855. }
  856. private static void _fullCleanup() {
  857. _partialCleanup();
  858. silenceMode = SILENCE_NORMAL;
  859. nextSong = null;
  860. postPlayPending = false;
  861. Equalizer.release();
  862. BassBoost.release();
  863. Virtualizer.release();
  864. }
  865. private static void _initializePlayers() {
  866. if (player == null) {
  867. player = _createPlayer();
  868. if (nextPlayer != null) {
  869. player.setAudioSessionId(nextPlayer.getAudioSessionId());
  870. } else {
  871. nextPlayer = _createPlayer();
  872. nextPlayer.setAudioSessionId(player.getAudioSessionId());
  873. }
  874. } else if (nextPlayer == null) {
  875. nextPlayer = _createPlayer();
  876. nextPlayer.setAudioSessionId(player.getAudioSessionId());
  877. }
  878. }
  879. private static boolean _requestFocus() {
  880. if (thePlayer == null || audioManager == null || audioManager.requestAudioFocus(thePlayer, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN) != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
  881. hasFocus = false;
  882. return false;
  883. }
  884. localHandler.sendEmptyMessageAtTime(MSG_REGISTER_MEDIA_BUTTON_EVENT_RECEIVER, SystemClock.uptimeMillis());
  885. hasFocus = true;
  886. volumeDimmed = false;
  887. return true;
  888. }
  889. private static void _storeSongTime() {
  890. try {
  891. if (song == null || song.isHttp)
  892. storedSongTime = -1;
  893. else if (player != null && playerState == PLAYER_STATE_LOADED)
  894. storedSongTime = player.getCurrentPosition();
  895. } catch (Throwable ex) {
  896. ex.printStackTrace();
  897. }
  898. }
  899. private static void _prepareNextPlayer(Song song) {
  900. try {
  901. if (song == nextSongScheduledForPreparation && nextPlayer != null) {
  902. //Even though it happens very rarely, a few devices will freeze and produce an ANR
  903. //when calling setDataSource from the main thread :(
  904. nextPlayer.setDataSource(nextSongScheduledForPreparation.path);
  905. nextPlayer.prepareAsync();
  906. nextPlayerState = PLAYER_STATE_PREPARING;
  907. }
  908. } catch (Throwable ex) {
  909. nextPlayerState = PLAYER_STATE_NEW;
  910. ex.printStackTrace();
  911. }
  912. }
  913. private static void _scheduleNextPlayerForPreparation() {
  914. nextAlreadySetForPlaying = false;
  915. prepareNextAfterSeeking = false;
  916. nextPlayerState = PLAYER_STATE_NEW;
  917. if (handler != null && song != null && nextSong != null && !nextSong.isHttp && nextPreparationEnabled && song.lengthMS > 10000 && nextSong.lengthMS > 10000) {
  918. handler.removeMessages(MSG_PREPARE_NEXT_SONG);
  919. nextSongScheduledForPreparation = nextSong;
  920. handler.sendMessageAtTime(Message.obtain(handler, MSG_PREPARE_NEXT_SONG, nextSong), SystemClock.uptimeMillis() + 5000);
  921. }
  922. }
  923. private static void _handleFailure(Throwable ex, boolean checkForPermission) {
  924. if (checkForPermission) {
  925. checkForPermission = false;
  926. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  927. if (thePlayer.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
  928. checkForPermission = true;
  929. }
  930. }
  931. if (songWhenFirstErrorHappened == song || howThePlayerStarted != SongList.HOW_NEXT_AUTO || checkForPermission) {
  932. songWhenFirstErrorHappened = null;
  933. _updateState(false, checkForPermission ? new PermissionDeniedException() : ex);
  934. } else {
  935. if (songWhenFirstErrorHappened == null)
  936. songWhenFirstErrorHappened = song;
  937. _updateState(false, null);
  938. localHandler.sendMessageAtTime(Message.obtain(localHandler, MSG_PRE_PLAY, SongList.HOW_NEXT_AUTO, 0), SystemClock.uptimeMillis());
  939. }
  940. }
  941. private static void _postPlay(int how, Song[] songArray) {
  942. if (state != STATE_ALIVE)
  943. return;
  944. song = songArray[0];
  945. if (!hasFocus && !_requestFocus()) {
  946. _fullCleanup();
  947. _updateState(false, new FocusException());
  948. return;
  949. }
  950. //we must set this to false here, as the user could have manually
  951. //started playback before the focus timer had the chance to trigger
  952. resumePlaybackAfterFocusGain = false;
  953. if (song == null) {
  954. storedSongTime = -1;
  955. songWhenFirstErrorHappened = null;
  956. _fullCleanup();
  957. _updateState(false, null);
  958. return;
  959. }
  960. try {
  961. howThePlayerStarted = how;
  962. playerBuffering = false;
  963. if (playerState == PLAYER_STATE_PREPARING)
  964. //probably the user is changing songs too fast!
  965. _partialCleanup();
  966. if (player == null || nextPlayer == null) {
  967. _initializePlayers();
  968. //don't even ask.......
  969. //(a few devices won't disable one effect while the other effect is enabled)
  970. Equalizer.release();
  971. BassBoost.release();
  972. Virtualizer.release();
  973. final int sessionId = player.getAudioSessionId();
  974. if (Equalizer.isEnabled()) {
  975. Equalizer.initialize(sessionId);
  976. Equalizer.setEnabled(true);
  977. }
  978. if (BassBoost.isEnabled()) {
  979. BassBoost.initialize(sessionId);
  980. BassBoost.setEnabled(true);
  981. }
  982. if (Virtualizer.isEnabled()) {
  983. Virtualizer.initialize(sessionId);
  984. Virtualizer.setEnabled(true);
  985. }
  986. }
  987. player.reset();
  988. postPlayPending = false;
  989. if (nextSong == song && how != SongList.HOW_CURRENT) {
  990. storedSongTime = -1;
  991. if (nextPlayerState == PLAYER_STATE_LOADED) {
  992. playerState = PLAYER_STATE_LOADED;
  993. final MediaPlayer p = player;
  994. player = nextPlayer;
  995. nextPlayer = p;
  996. nextSong = songArray[1];
  997. if (!nextAlreadySetForPlaying || how != SongList.HOW_NEXT_AUTO) {
  998. _startPlayer();
  999. } else {
  1000. playing = true;
  1001. //when dimmed, decreased the volume by 20dB
  1002. final float multiplier = (volumeDimmed ? (volumeMultiplier * 0.1f) : volumeMultiplier);
  1003. player.setVolume(multiplier, multiplier);
  1004. }
  1005. songWhenFirstErrorHappened = null;
  1006. _scheduleNextPlayerForPreparation();
  1007. _updateState(false, null);
  1008. return;
  1009. } else {
  1010. //just wait for the next song to finish preparing
  1011. if (nextPlayerState == PLAYER_STATE_PREPARING) {
  1012. playing = false;
  1013. playerState = PLAYER_STATE_PREPARING;
  1014. nextPlayerState = PLAYER_STATE_NEW;
  1015. final MediaPlayer p = player;
  1016. player = nextPlayer;
  1017. nextPlayer = p;
  1018. nextSong = songArray[1];
  1019. _updateState(false, null);
  1020. return;
  1021. }
  1022. }
  1023. }
  1024. playing = false;
  1025. playerState = PLAYER_STATE_PREPARING;
  1026. if (how != SongList.HOW_CURRENT)
  1027. storedSongTime = -1;
  1028. if (song.path == null || song.path.length() == 0)
  1029. throw new IOException();
  1030. songScheduledForPreparation = song;
  1031. //Even though it happens very rarely, a few devices will freeze and produce an ANR
  1032. //when calling setDataSource from the main thread :(
  1033. player.setDataSource(song.path);
  1034. player.prepareAsync();
  1035. nextPlayerState = PLAYER_STATE_NEW;
  1036. nextPlayer.reset();
  1037. nextSong = songArray[1];
  1038. nextSongScheduledForPreparation = null;
  1039. if (nextPreparationEnabled)
  1040. handler.removeMessages(MSG_PREPARE_NEXT_SONG);
  1041. _updateState(false, null);
  1042. } catch (Throwable ex) {
  1043. _fullCleanup();
  1044. //clear the flag here to allow _handleFailure to move to the next song
  1045. _handleFailure(ex, true);
  1046. }
  1047. }
  1048. private static void _playPause() {
  1049. if (state != STATE_ALIVE || playerState == PLAYER_STATE_PREPARING)
  1050. return;
  1051. try {
  1052. //we must set this to false here, as the user could have manually
  1053. //started playback before the focus timer had the chance to trigger
  1054. resumePlaybackAfterFocusGain = false;
  1055. if (playing) {
  1056. playing = false;
  1057. player.pause();
  1058. silenceMode = SILENCE_NORMAL;
  1059. _storeSongTime();
  1060. _updateState(false, null);
  1061. } else {
  1062. if (song == null || playerState == PLAYER_STATE_NEW || !hasFocus) {
  1063. localHandler.sendMessageAtTime(Message.obtain(localHandler, MSG_PRE_PLAY, SongList.HOW_CURRENT, 0), SystemClock.uptimeMillis());
  1064. } else {
  1065. howThePlayerStarted = SongList.HOW_CURRENT;
  1066. _startPlayer();
  1067. _updateState(false, null);
  1068. }
  1069. }
  1070. } catch (Throwable ex) {
  1071. _fullCleanup();
  1072. _updateState(false, ex);
  1073. }
  1074. }
  1075. private static void _seekTo(int timeMS) {
  1076. if (state != STATE_ALIVE || playerState == PLAYER_STATE_PREPARING)
  1077. return;
  1078. storedSongTime = timeMS;
  1079. if (playerState == PLAYER_STATE_LOADED) {
  1080. playAfterSeeking = playing;
  1081. playerState = PLAYER_STATE_PREPARING;
  1082. try {
  1083. if (playing) {
  1084. playing = false;
  1085. player.pause();
  1086. }
  1087. player.seekTo(timeMS);
  1088. _updateState(false, null);
  1089. } catch (Throwable ex) {
  1090. _fullCleanup();
  1091. _updateState(false, ex);
  1092. }
  1093. }
  1094. }
  1095. private static void _processFadeInVolumeTimer(int increment) {
  1096. volumeDBFading += increment;
  1097. //magnitude = 10 ^ (dB/20)
  1098. //x^p = a ^ (p * log a (x))
  1099. //10^p = e ^ (p * log e (10))
  1100. float multiplier = (float)Math.exp((double)volumeDBFading * 2.3025850929940456840179914546844 / 2000.0);
  1101. boolean send = true;
  1102. if (multiplier >= volumeMultiplier) {
  1103. multiplier = volumeMultiplier;
  1104. send = false;
  1105. }
  1106. //when dimmed, decreased the volume by 20dB
  1107. if (volumeDimmed)
  1108. multiplier *= 0.1f;
  1109. if (handler != null && hasFocus && player != null && playerState == PLAYER_STATE_LOADED) {
  1110. try {
  1111. player.setVolume(multiplier, multiplier);
  1112. } catch (Throwable ex) {
  1113. ex.printStackTrace();
  1114. }
  1115. if (send)
  1116. handler.sendMessageAtTime(Message.obtain(handler, MSG_FADE_IN_VOLUME_TIMER, increment, 0), SystemClock.uptimeMillis() + 50);
  1117. }
  1118. }
  1119. private static void _syncVolume() {
  1120. if (state != STATE_ALIVE)
  1121. return;
  1122. if (volumeControlType == VOLUME_CONTROL_STREAM) {
  1123. final int volumeStream = localVolumeStream;
  1124. try {
  1125. if (audioManager != null)
  1126. audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, ((volumeStream <= 0) ? 0 : ((volumeStream >= volumeStreamMax) ? volumeStreamMax : volumeStream)), 0);
  1127. } catch (Throwable ex) {
  1128. ex.printStackTrace();
  1129. }
  1130. return;
  1131. }
  1132. if (volumeDB == localVolumeDB)
  1133. return;
  1134. volumeDB = localVolumeDB;
  1135. volumeMultiplier = ((volumeDB <= VOLUME_MIN_DB) ? 0.0f : ((volumeDB >= 0) ? 1.0f : (float)Math.exp((double)volumeDB * 2.3025850929940456840179914546844 / 2000.0)));
  1136. //when dimmed, decreased the volume by 20dB
  1137. final float multiplier = (volumeDimmed ? (volumeMultiplier * 0.1f) : volumeMultiplier);
  1138. if (player != null) {
  1139. try {
  1140. player.setVolume(multiplier, multiplier);
  1141. } catch (Throwable ex) {
  1142. ex.printStackTrace();
  1143. }
  1144. }
  1145. if (nextPlayer != null) {
  1146. try {
  1147. nextPlayer.setVolume(multiplier, multiplier);
  1148. } catch (Throwable ex) {
  1149. ex.printStackTrace();
  1150. }
  1151. }
  1152. }
  1153. private static void _overrideVolumeMultiplier(boolean toMax) {
  1154. if (state != STATE_ALIVE)
  1155. return;
  1156. volumeMultiplier = ((toMax || (volumeDB >= 0)) ? 1.0f : ((volumeDB <= VOLUME_MIN_DB) ? 0.0f : (float)Math.exp((double)volumeDB * 2.3025850929940456840179914546844 / 2000.0)));
  1157. //when dimmed, decreased the volume by 20dB
  1158. final float multiplier = (volumeDimmed ? (volumeMultiplier * 0.1f) : volumeMultiplier);
  1159. if (player != null) {
  1160. try {
  1161. player.setVolume(multiplier, multiplier);
  1162. } catch (Throwable ex) {
  1163. ex.printStackTrace();
  1164. }
  1165. }
  1166. if (nextPlayer != null) {
  1167. try {
  1168. nextPlayer.setVolume(multiplier, multiplier);
  1169. } catch (Throwable ex) {
  1170. ex.printStackTrace();
  1171. }
  1172. }
  1173. }
  1174. private static void _processFocusGainTimer() {
  1175. if (state != STATE_ALIVE || !hasFocus)
  1176. return;
  1177. //someone else may have changed our values if the engine is shared
  1178. localHandler.sendEmptyMessageAtTime(MSG_REGISTER_MEDIA_BUTTON_EVENT_RECEIVER, SystemClock.uptimeMillis());
  1179. if (resumePlaybackAfterFocusGain) {
  1180. //do not restart playback in scenarios like this (it really scares people!):
  1181. //the person has answered a call, removed the headset, ended the call without
  1182. //the headset plugged in, and then the focus came back to us
  1183. if (audioSinkBeforeFocusLoss != AUDIO_SINK_DEVICE && audioSink == AUDIO_SINK_DEVICE) {
  1184. resumePlaybackAfterFocusGain = false;
  1185. _requestFocus();
  1186. } else if (!playing) {
  1187. localHandler.sendMessageAtTime(Message.obtain(localHandler, MSG_PRE_PLAY, SongList.HOW_CURRENT, 0), SystemClock.uptimeMillis());
  1188. } else {
  1189. resumePlaybackAfterFocusGain = false;
  1190. }
  1191. }
  1192. }
  1193. private static void _enableEffects(int arg1, Runnable callback) {
  1194. //don't even ask.......
  1195. //(a few devices won't disable one effect while the other effect is enabled)
  1196. Equalizer.release();
  1197. BassBoost.release();
  1198. Virtualizer.release();
  1199. if ((arg1 & 1) != 0) {
  1200. if (player != null)
  1201. Equalizer.initialize(player.getAudioSessionId());
  1202. Equalizer.setEnabled(true);
  1203. } else {
  1204. Equalizer.setEnabled(false);
  1205. }
  1206. if ((arg1 & 2) != 0) {
  1207. if (player != null)
  1208. BassBoost.initialize(player.getAudioSessionId());
  1209. BassBoost.setEnabled(true);
  1210. } else {
  1211. BassBoost.setEnabled(false);
  1212. }
  1213. if ((arg1 & 4) != 0) {
  1214. if (player != null)
  1215. Virtualizer.initialize(player.getAudioSessionId());
  1216. Virtualizer.setEnabled(true);
  1217. } else {
  1218. Virtualizer.setEnabled(false);
  1219. }
  1220. if (callback != null)
  1221. MainHandler.postToMainThread(callback);
  1222. }
  1223. @Override
  1224. public void onAudioFocusChange(int focusChange) {
  1225. if (state != STATE_ALIVE)
  1226. return;
  1227. if (handler != null) {
  1228. handler.removeMessages(MSG_FOCUS_GAIN_TIMER);
  1229. handler.removeMessages(MSG_FADE_IN_VOLUME_TIMER);
  1230. }
  1231. if (localHandler != null)
  1232. localHandler.removeMessages(MSG_HEADSET_HOOK_TIMER);
  1233. volumeDimmed = false;
  1234. switch (focusChange) {
  1235. case AudioManager.AUDIOFOCUS_GAIN:
  1236. if (!hasFocus) {
  1237. hasFocus = true;
  1238. if (handler != null)
  1239. handler.sendMessageAtTime(Message.obtain(handler, MSG_FOCUS_GAIN_TIMER), SystemClock.uptimeMillis() + 1500);
  1240. } else {
  1241. //came here from AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
  1242. if (player != null && playerState == PLAYER_STATE_LOADED) {
  1243. try {
  1244. player.setVolume(volumeMultiplier, volumeMultiplier);
  1245. } catch (Throwable ex) {
  1246. ex.printStackTrace();
  1247. }
  1248. }
  1249. }
  1250. break;
  1251. case AudioManager.AUDIOFOCUS_LOSS:
  1252. case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
  1253. //we just cannot replace resumePlaybackAfterFocusGain's value with
  1254. //playing, because if a second focus loss occurred BEFORE _focusGain
  1255. //had a chance to be called, then playing would be false, when in fact,
  1256. //we want resumePlaybackAfterFocusGain to remain true (and the audio sink
  1257. //must be untouched)
  1258. //also because of that, we must take resumePlaybackAfterFocusGain into
  1259. //consideration here, because _fullCleanup always sets it to false
  1260. final boolean oldResumePlaybackAfterFocusGain = resumePlaybackAfterFocusGain;
  1261. final boolean resume = (localPlaying || localPlayerState == PLAYER_STATE_PREPARING);
  1262. hasFocus = false;
  1263. _storeSongTime();
  1264. _fullCleanup();
  1265. silenceMode = SILENCE_FOCUS;
  1266. if (resume || oldResumePlaybackAfterFocusGain)
  1267. resumePlaybackAfterFocusGain = true;
  1268. //change audioSinkBeforeFocusLoss only at the first time we lose focus!
  1269. //(if we lose focus several times in row, before we actually have had
  1270. //chance to play at least once, resume will be false)
  1271. if (resume)
  1272. audioSinkBeforeFocusLoss = audioSink;
  1273. _updateState(false, null);
  1274. break;
  1275. case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
  1276. hasFocus = true;
  1277. if (!doNotAttenuateVolume) {
  1278. //when dimmed, decreased the volume by 20dB
  1279. final float multiplier = volumeMultiplier * 0.1f;
  1280. volumeDimmed = true;
  1281. if (player != null && playerState == PLAYER_STATE_LOADED) {
  1282. try {
  1283. player.setVolume(multiplier, multiplier);
  1284. } catch (Throwable ex) {
  1285. ex.printStackTrace();
  1286. }
  1287. }
  1288. }
  1289. break;
  1290. }
  1291. }
  1292. @Override
  1293. public void onCompletion(MediaPlayer mediaPlayer) {
  1294. if (state != STATE_ALIVE)
  1295. return;
  1296. if (playing && player == mediaPlayer)
  1297. localHandler.sendMessageAtTime(Message.obtain(localHandler, MSG_PRE_PLAY, SongList.HOW_NEXT_AUTO, 0), SystemClock.uptimeMillis());
  1298. }
  1299. @Override
  1300. public boolean onError(MediaPlayer mediaPlayer, int what, int extra) {
  1301. if (state != STATE_ALIVE)
  1302. return true;
  1303. if (mediaPlayer == nextPlayer || mediaPlayer == player) {
  1304. if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
  1305. _storeSongTime();
  1306. _fullCleanup();
  1307. if (reviveAlreadyTried) {
  1308. reviveAlreadyTried = false;
  1309. _updateState(false, new MediaServerDiedException());
  1310. } else {
  1311. reviveAlreadyTried = true;
  1312. localHandler.sendMessageAtTime(Message.obtain(localHandler, MSG_PRE_PLAY, SongList.HOW_CURRENT, 0), SystemClock.uptimeMillis());
  1313. }
  1314. } else if (mediaPlayer == nextPlayer) {
  1315. nextSong = null;
  1316. nextPlayerState = PLAYER_STATE_NEW;
  1317. } else {
  1318. final boolean prep = (playerState == PLAYER_STATE_PREPARING);
  1319. _fullCleanup();
  1320. if (prep && howThePlayerStarted == SongList.HOW_NEXT_AUTO)
  1321. //the error happened during currentSong's preparation
  1322. _handleFailure((extra == MediaPlayer.MEDIA_ERROR_TIMED_OUT) ? new TimeoutException() : new IOException(), false);
  1323. else
  1324. _updateState(false, (extra == MediaPlayer.MEDIA_ERROR_TIMED_OUT) ? new TimeoutException() : new IOException());
  1325. }
  1326. } else {
  1327. _fullCleanup();
  1328. _updateState(false, new Exception("Invalid MediaPlayer"));
  1329. }
  1330. return true;
  1331. }
  1332. @Override
  1333. public boolean onInfo(MediaPlayer mediaPlayer, int what, int extra) {
  1334. if (mediaPlayer == player) {
  1335. switch (what) {
  1336. case MediaPlayer.MEDIA_INFO_BUFFERING_START:
  1337. if (!playerBuffering) {
  1338. playerBuffering = true;
  1339. _updateState(true, null);
  1340. }
  1341. break;
  1342. case MediaPlayer.MEDIA_INFO_BUFFERING_END:
  1343. if (playerBuffering) {
  1344. playerBuffering = false;
  1345. _updateState(true, null);
  1346. }
  1347. break;
  1348. }
  1349. }
  1350. return false;
  1351. }
  1352. @Override
  1353. public void onPrepared(MediaPlayer mediaPlayer) {
  1354. if (state != STATE_ALIVE)
  1355. return;
  1356. if (mediaPlayer == nextPlayer) {
  1357. if (nextSongScheduledForPreparation == nextSong && nextSong != null) {
  1358. nextSongScheduledForPreparation = null;
  1359. nextPlayerState = PLAYER_STATE_LOADED;

Large files files are truncated, but you can click here to view the full file