PageRenderTime 68ms 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
  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;
  1360. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
  1361. _setNextPlayer();
  1362. }
  1363. } else if (mediaPlayer == player) {
  1364. if (songScheduledForPreparation == song && song != null) {
  1365. songScheduledForPreparation = null;
  1366. playerState = PLAYER_STATE_LOADED;
  1367. try {
  1368. if (!hasFocus) {
  1369. _fullCleanup();
  1370. _updateState(false, null);
  1371. return;
  1372. }
  1373. //when dimmed, decreased the volume by 20dB
  1374. float multiplier = (volumeDimmed ? (volumeMultiplier * 0.1f) : volumeMultiplier);
  1375. player.setVolume(multiplier, multiplier);
  1376. if (storedSongTime < 0 || song.isHttp) {
  1377. storedSongTime = -1;
  1378. _startPlayer();
  1379. _scheduleNextPlayerForPreparation();
  1380. } else {
  1381. playAfterSeeking = true;
  1382. prepareNextAfterSeeking = true;
  1383. playerState = PLAYER_STATE_PREPARING;
  1384. player.seekTo(storedSongTime);
  1385. }
  1386. songWhenFirstErrorHappened = null;
  1387. _updateState(false, null);
  1388. } catch (Throwable ex) {
  1389. _fullCleanup();
  1390. _handleFailure(ex, false);
  1391. }
  1392. }
  1393. } else {
  1394. _fullCleanup();
  1395. _updateState(false, new Exception("Invalid MediaPlayer"));
  1396. }
  1397. }
  1398. @Override
  1399. public void onSeekComplete(MediaPlayer mediaPlayer) {
  1400. if (state != STATE_ALIVE)
  1401. return;
  1402. if (mediaPlayer == player) {
  1403. try {
  1404. playerState = PLAYER_STATE_LOADED;
  1405. if (playAfterSeeking)
  1406. _startPlayer();
  1407. _updateState(false, null);
  1408. if (prepareNextAfterSeeking)
  1409. _scheduleNextPlayerForPreparation();
  1410. } catch (Throwable ex) {
  1411. _fullCleanup();
  1412. _updateState(false, ex);
  1413. }
  1414. }
  1415. }
  1416. //I know this is far from "organized"... but this is the only way to prevent
  1417. //the class BluetoothVisualizerController from loading into memory without need!!!
  1418. public static Object bluetoothVisualizerController;
  1419. //bluetoothVisualizerConfig bits:
  1420. //0 1 2 = size
  1421. //3 4 = speed
  1422. //5 6 7 8 = frames to skip (index)
  1423. //9 = vu meter
  1424. public static int bluetoothVisualizerLastErrorMessage, bluetoothVisualizerConfig, bluetoothVisualizerState;
  1425. public static final int BLUETOOTH_VISUALIZER_STATE_INITIAL = 0;
  1426. public static final int BLUETOOTH_VISUALIZER_STATE_CONNECTING = 1;
  1427. public static final int BLUETOOTH_VISUALIZER_STATE_CONNECTED = 2;
  1428. public static final int BLUETOOTH_VISUALIZER_STATE_TRANSMITTING = 3;
  1429. public static boolean startBluetoothVisualizer(ActivityHost activity, boolean startTransmissionOnConnection) {
  1430. if (state != STATE_ALIVE)
  1431. return false;
  1432. stopBluetoothVisualizer();
  1433. final BluetoothVisualizerControllerJni b = new BluetoothVisualizerControllerJni(activity, startTransmissionOnConnection);
  1434. if (bluetoothVisualizerLastErrorMessage == 0) {
  1435. bluetoothVisualizerController = b;
  1436. return true;
  1437. }
  1438. b.destroy();
  1439. return false;
  1440. }
  1441. public static void stopBluetoothVisualizer() {
  1442. bluetoothVisualizerState = BLUETOOTH_VISUALIZER_STATE_INITIAL;
  1443. if (bluetoothVisualizerController != null) {
  1444. final BluetoothVisualizerControllerJni b = (BluetoothVisualizerControllerJni)bluetoothVisualizerController;
  1445. bluetoothVisualizerController = null;
  1446. b.destroy();
  1447. }
  1448. }
  1449. private static void updateBluetoothVisualizer(boolean songHasChanged) {
  1450. if (bluetoothVisualizerController != null) {
  1451. final BluetoothVisualizerControllerJni b = (BluetoothVisualizerControllerJni)bluetoothVisualizerController;
  1452. if (!songHasChanged && playing)
  1453. b.resetAndResume();
  1454. else
  1455. b.resume();
  1456. b.playingChanged();
  1457. }
  1458. }
  1459. public static int getBluetoothVisualizerSize() {
  1460. final int size = (bluetoothVisualizerConfig & 7);
  1461. return ((size <= 0) ? 0 : ((size >= 6) ? 6 : size));
  1462. }
  1463. public static void setBluetoothVisualizerSize(int size) {
  1464. bluetoothVisualizerConfig = (bluetoothVisualizerConfig & (~7)) | ((size <= 0) ? 0 : ((size >= 6) ? 6 : size));
  1465. }
  1466. public static int getBluetoothVisualizerSpeed() {
  1467. final int speed = ((bluetoothVisualizerConfig >> 3) & 3);
  1468. return ((speed <= 0) ? 0 : ((speed >= 2) ? 2 : 1));
  1469. }
  1470. public static void setBluetoothVisualizerSpeed(int speed) {
  1471. bluetoothVisualizerConfig = (bluetoothVisualizerConfig & (~(3 << 3))) | (((speed <= 0) ? 0 : ((speed >= 2) ? 2 : 1)) << 3);
  1472. }
  1473. public static int getBluetoothVisualizerFramesPerSecond(int framesToSkipIndex) {
  1474. //index 0, 1, 2, 3, 4, 5, 6, 7 , 8 , 9 , 10, 11
  1475. //frames to skip 0, 1, 2, 3, 4, 5, 9, 11, 14, 19, 29, 59
  1476. //frames/second 60,30,20,15,12,10, 6, 5 , 4 , 3 , 2 , 1
  1477. return ((framesToSkipIndex <= 5) ? (60 / (framesToSkipIndex + 1)) : (12 - framesToSkipIndex));
  1478. }
  1479. public static int getBluetoothVisualizerFramesToSkip() {
  1480. return (getBluetoothVisualizerFramesPerSecond(11 - getBluetoothVisualizerFramesToSkipIndex()) - 1);
  1481. }
  1482. public static int getBluetoothVisualizerFramesToSkipIndex() {
  1483. final int framesToSkipIndex = ((bluetoothVisualizerConfig >> 5) & 15);
  1484. return ((framesToSkipIndex <= 0) ? 0 : ((framesToSkipIndex >= 11) ? 11 : framesToSkipIndex));
  1485. }
  1486. public static void setBluetoothVisualizerFramesToSkipIndex(int framesToSkipIndex) {
  1487. bluetoothVisualizerConfig = (bluetoothVisualizerConfig & (~(15 << 5))) | (((framesToSkipIndex <= 0) ? 0 : ((framesToSkipIndex >= 11) ? 11 : framesToSkipIndex)) << 5);
  1488. }
  1489. public static boolean isBluetoothUsingVUMeter() {
  1490. return ((bluetoothVisualizerConfig & (1 << 9)) != 0);
  1491. }
  1492. public static void setBluetoothUsingVUMeter(boolean usingVUMeter) {
  1493. if (usingVUMeter)
  1494. bluetoothVisualizerConfig |= (1 << 9);
  1495. else
  1496. bluetoothVisualizerConfig &= ~(1 << 9);
  1497. }
  1498. //these options were deprecated on version 74 in favor of their bit equivalents
  1499. private static final int OPT_VOLUME = 0x0000;
  1500. //private static final int OPT_CONTROLMODE = 0x0001;
  1501. private static final int OPT_LASTTIME = 0x0002;
  1502. private static final int OPT_PATH = 0x0003;
  1503. private static final int OPT_ORIGINALPATH = 0x0004;
  1504. private static final int OPT_FAVORITEFOLDERCOUNT = 0x0005;
  1505. //private static final int OPT_BASSBOOSTMODE = 0x0006;
  1506. private static final int OPT_FADEININCREMENTONFOCUS = 0x0007;
  1507. private static final int OPT_FADEININCREMENTONPAUSE = 0x0008;
  1508. private static final int OPT_FADEININCREMENTONOTHER = 0x0009;
  1509. //private static final int OPT_NEXTPREPARATION = 0x000a;
  1510. //private static final int OPT_PLAYFOLDERCLEARSLIST = 0x000b;
  1511. //private static final int OPT_KEEPSCREENON = 0x000c;
  1512. private static final int OPT_FORCEDORIENTATION = 0x000d;
  1513. private static final int OPT_DISPLAYVOLUMEINDB = 0x000e;
  1514. //private static final int OPT_DOUBLECLICKMODE = 0x000f;
  1515. //these 3 will no longer be used!
  1516. //private static final int OPT_MSGADDSHOWN = 0x0010;
  1517. //private static final int OPT_MSGPLAYSHOWN = 0x0011;
  1518. //private static final int OPT_MSGSTARTUPSHOWN = 0x0012;
  1519. //private static final int OPT_MARQUEETITLE = 0x0013;
  1520. private static final int OPT_VOLUMECONTROLTYPE = 0x0014;
  1521. //private static final int OPT_BLOCKBACKKEY = 0x0015;
  1522. private static final int OPT_TURNOFFTIMERCUSTOMMINUTES = 0x0016;
  1523. //private static final int OPT_ISDIVIDERVISIBLE = 0x0017;
  1524. //private static final int OPT_ISVERTICALMARGINLARGE = 0x0018;
  1525. //private static final int OPT_HANDLECALLKEY = 0x0019;
  1526. //private static final int OPT_PLAYWHENHEADSETPLUGGED = 0x001a;
  1527. //private static final int OPT_USEALTERNATETYPEFACE = 0x001b;
  1528. //private static final int OPT_GOBACKWHENPLAYINGFOLDERS = 0x001c;
  1529. //private static final int OPT_RANDOMMODE = 0x001d;
  1530. private static final int OPT_FORCEDLOCALE = 0x001e;
  1531. private static final int OPT_THEME = 0x001f;
  1532. private static final int OPT_MSGS = 0x0020;
  1533. private static final int OPT_MSGSTARTUP = 0x0021;
  1534. //private static final int OPT_WIDGETTRANSPARENTBG = 0x0022;
  1535. private static final int OPT_WIDGETTEXTCOLOR = 0x0023;
  1536. private static final int OPT_WIDGETICONCOLOR = 0x0024;
  1537. private static final int OPT_CUSTOMCOLORS = 0x0025;
  1538. private static final int OPT_LASTVERSIONCODE = 0x0026;
  1539. //private static final int OPT_BACKKEYALWAYSRETURNSTOPLAYERWHENBROWSING = 0x0027;
  1540. //private static final int OPT_WRAPAROUNDLIST = 0x0028;
  1541. //private static final int OPT_EXTRASPACING = 0x0029;
  1542. //private static final int OPT_OLDBROWSERBEHAVIOR = 0x002A;
  1543. //private static final int OPT_VISUALIZERORIENTATION = 0x002B;
  1544. private static final int OPT_SONGEXTRAINFOMODE = 0x002C;
  1545. private static final int OPT_TURNOFFTIMERSELECTEDMINUTES = 0x002D;
  1546. private static final int OPT_IDLETURNOFFTIMERCUSTOMMINUTES = 0x002E;
  1547. private static final int OPT_IDLETURNOFFTIMERSELECTEDMINUTES = 0x002F;
  1548. //private static final int OPT_FLAT = 0x0030;
  1549. //private static final int OPT_ALBUMART = 0x0031;
  1550. private static final int OPT_RADIOSEARCHTERM = 0x0032;
  1551. private static final int OPT_RADIOLASTGENRE = 0x0033;
  1552. private static final int OPT_TRANSITION = 0x0034;
  1553. private static final int OPT_BLUETOOTHVISUALIZERCONFIG = 0x0035;
  1554. //values 0x01xx are shared among all effects
  1555. static final int OPT_EQUALIZER_ENABLED = 0x0100;
  1556. static final int OPT_EQUALIZER_PRESET = 0x0101;
  1557. static final int OPT_EQUALIZER_LEVELCOUNT = 0x0102;
  1558. static final int OPT_EQUALIZER_LEVEL0 = 0x20000;
  1559. static final int OPT_BASSBOOST_ENABLED = 0x0110;
  1560. static final int OPT_BASSBOOST_STRENGTH = 0x0111;
  1561. static final int OPT_VIRTUALIZER_ENABLED = 0x0112;
  1562. static final int OPT_VIRTUALIZER_STRENGTH = 0x0113;
  1563. private static final int OPTBIT_CONTROLMODE = 0;
  1564. private static final int OPTBIT_BASSBOOSTMODE = 1;
  1565. private static final int OPTBIT_NEXTPREPARATION = 2;
  1566. private static final int OPTBIT_PLAYFOLDERCLEARSLIST = 3;
  1567. private static final int OPTBIT_KEEPSCREENON = 4;
  1568. //volume control changed on version 71
  1569. private static final int OPTBIT_DISPLAYVOLUMEINDB = 5;
  1570. private static final int OPTBIT_VOLUMECONTROLTYPE0 = 5;
  1571. private static final int OPTBIT_DOUBLECLICKMODE = 6;
  1572. private static final int OPTBIT_MARQUEETITLE = 7;
  1573. private static final int OPTBIT_FLAT = 8;
  1574. private static final int OPTBIT_ALBUMART = 9;
  1575. private static final int OPTBIT_BLOCKBACKKEY = 10;
  1576. private static final int OPTBIT_ISDIVIDERVISIBLE = 11;
  1577. private static final int OPTBIT_ISVERTICALMARGINLARGE = 12;
  1578. private static final int OPTBIT_HANDLECALLKEY = 13;
  1579. private static final int OPTBIT_PLAYWHENHEADSETPLUGGED = 14;
  1580. private static final int OPTBIT_USEALTERNATETYPEFACE = 15;
  1581. private static final int OPTBIT_GOBACKWHENPLAYINGFOLDERS = 16;
  1582. private static final int OPTBIT_RANDOMMODE = 17;
  1583. private static final int OPTBIT_WIDGETTRANSPARENTBG = 18;
  1584. private static final int OPTBIT_BACKKEYALWAYSRETURNSTOPLAYERWHENBROWSING = 19;
  1585. private static final int OPTBIT_WRAPAROUNDLIST = 20;
  1586. private static final int OPTBIT_EXTRASPACING = 21;
  1587. //this bit has been recycled on version 71
  1588. //private static final int OPTBIT_OLDBROWSERBEHAVIOR = 22;
  1589. private static final int OPTBIT_VOLUMECONTROLTYPE1 = 22;
  1590. static final int OPTBIT_EQUALIZER_ENABLED = 23;
  1591. static final int OPTBIT_BASSBOOST_ENABLED = 24;
  1592. static final int OPTBIT_VIRTUALIZER_ENABLED = 25;
  1593. private static final int OPTBIT_HEADSETHOOK_DOUBLE_PRESS_PAUSES = 26;
  1594. private static final int OPTBIT_DO_NOT_ATTENUATE_VOLUME = 27;
  1595. private static final int OPTBIT_SCROLLBAR_TO_THE_LEFT = 28;
  1596. private static final int OPTBIT_SCROLLBAR_SONGLIST0 = 29;
  1597. private static final int OPTBIT_SCROLLBAR_SONGLIST1 = 30;
  1598. private static final int OPTBIT_SCROLLBAR_BROWSER0 = 31;
  1599. private static final int OPTBIT_SCROLLBAR_BROWSER1 = 32;
  1600. private static final int OPTBIT_LASTRADIOSEARCHWASBYGENRE = 33;
  1601. private static final int OPTBIT_EXPANDSEEKBAR = 34;
  1602. private static final int OPTBIT_REPEATONE = 35;
  1603. private static final int OPTBIT_NOTFULLSCREEN = 36;
  1604. private static final int OPTBIT_CONTROLS_TO_THE_LEFT = 37;
  1605. private static final int OPTBIT_BORDERS = 38;
  1606. private static final int OPTBIT_ANIMATIONS = 39;
  1607. private static final int OPTBIT_VISUALIZER_PORTRAIT = 40;
  1608. private static final int OPTBIT_REPEATNONE = 41;
  1609. private static final int OPTBIT_TURNOFFPLAYLIST = 42;
  1610. private static final int OPT_FAVORITEFOLDER0 = 0x10000;
  1611. private static Notification notification;
  1612. private static RemoteViews notificationRemoteViews;
  1613. private static boolean appNotInForeground, idleTurnOffTimerSent;
  1614. private static long turnOffTimerOrigin, idleTurnOffTimerOrigin, headsetHookLastTime;
  1615. private static final HashSet<String> favoriteFolders = new HashSet<>();
  1616. private static PendingIntent intentActivityHost, intentPrevious, intentPlayPause, intentNext, intentExit;
  1617. public static String path, originalPath, radioSearchTerm;
  1618. public static boolean lastRadioSearchWasByGenre, nextPreparationEnabled, doNotAttenuateVolume, headsetHookDoublePressPauses, clearListWhenPlayingFolders, controlMode, bassBoostMode, handleCallKey, playWhenHeadsetPlugged, goBackWhenPlayingFolders, turnOffWhenPlaylistEnds;
  1619. public static int radioLastGenre, fadeInIncrementOnFocus, fadeInIncrementOnPause, fadeInIncrementOnOther, turnOffTimerCustomMinutes, turnOffTimerSelectedMinutes, idleTurnOffTimerCustomMinutes, idleTurnOffTimerSelectedMinutes;
  1620. public static SerializableMap loadConfigFromFile(Context context) {
  1621. final SerializableMap opts = SerializableMap.deserialize(context, "_Player");
  1622. return ((opts == null) ? new SerializableMap() : opts);
  1623. }
  1624. private static void loadConfig(Context context) {
  1625. final SerializableMap opts = loadConfigFromFile(context);
  1626. UI.lastVersionCode = opts.getInt(OPT_LASTVERSIONCODE, 0);
  1627. volumeDB = opts.getInt(OPT_VOLUME);
  1628. if (volumeDB < VOLUME_MIN_DB)
  1629. volumeDB = VOLUME_MIN_DB;
  1630. else if (volumeDB > 0)
  1631. volumeDB = 0;
  1632. localVolumeDB = volumeDB;
  1633. volumeMultiplier = ((volumeDB <= VOLUME_MIN_DB) ? 0.0f : ((volumeDB >= 0) ? 1.0f : (float)Math.exp((double)volumeDB * 2.3025850929940456840179914546844 / 2000.0)));
  1634. path = opts.getString(OPT_PATH);
  1635. originalPath = opts.getString(OPT_ORIGINALPATH);
  1636. storedSongTime = opts.getInt(OPT_LASTTIME, -1);
  1637. fadeInIncrementOnFocus = opts.getInt(OPT_FADEININCREMENTONFOCUS, 125);
  1638. fadeInIncrementOnPause = opts.getInt(OPT_FADEININCREMENTONPAUSE, 125);
  1639. fadeInIncrementOnOther = opts.getInt(OPT_FADEININCREMENTONOTHER, 0);
  1640. UI.forcedOrientation = opts.getInt(OPT_FORCEDORIENTATION);
  1641. turnOffTimerCustomMinutes = opts.getInt(OPT_TURNOFFTIMERCUSTOMMINUTES, 30);
  1642. if (turnOffTimerCustomMinutes < 1)
  1643. turnOffTimerCustomMinutes = 1;
  1644. turnOffTimerSelectedMinutes = opts.getInt(OPT_TURNOFFTIMERSELECTEDMINUTES, 0);
  1645. if (turnOffTimerSelectedMinutes < 0)
  1646. turnOffTimerSelectedMinutes = 0;
  1647. idleTurnOffTimerCustomMinutes = opts.getInt(OPT_IDLETURNOFFTIMERCUSTOMMINUTES, 30);
  1648. if (idleTurnOffTimerCustomMinutes < 1)
  1649. idleTurnOffTimerCustomMinutes = 1;
  1650. idleTurnOffTimerSelectedMinutes = opts.getInt(OPT_IDLETURNOFFTIMERSELECTEDMINUTES, 0);
  1651. if (idleTurnOffTimerSelectedMinutes < 0)
  1652. idleTurnOffTimerSelectedMinutes = 0;
  1653. UI.customColors = opts.getBuffer(OPT_CUSTOMCOLORS, null);
  1654. UI.setTheme(null, (UI.lastVersionCode < 74) ? UI.THEME_FPLAY : opts.getInt(OPT_THEME, UI.THEME_FPLAY));
  1655. UI.msgs = opts.getInt(OPT_MSGS, 0);
  1656. UI.msgStartup = opts.getInt(OPT_MSGSTARTUP, 0);
  1657. UI.widgetTextColor = opts.getInt(OPT_WIDGETTEXTCOLOR, 0xff000000);
  1658. UI.widgetIconColor = opts.getInt(OPT_WIDGETICONCOLOR, 0xff000000);
  1659. bluetoothVisualizerConfig = opts.getInt(OPT_BLUETOOTHVISUALIZERCONFIG, 2 | (2 << 3) | (3 << 5));
  1660. Song.extraInfoMode = opts.getInt(OPT_SONGEXTRAINFOMODE, Song.EXTRA_ARTIST);
  1661. radioSearchTerm = opts.getString(OPT_RADIOSEARCHTERM);
  1662. radioLastGenre = opts.getInt(OPT_RADIOLASTGENRE, 21);
  1663. UI.setTransition((UI.lastVersionCode < 76 && UI.deviceSupportsAnimations) ? UI.TRANSITION_FADE : opts.getInt(OPT_TRANSITION, UI.deviceSupportsAnimations ? UI.TRANSITION_FADE : UI.TRANSITION_NONE));
  1664. //the volume control types changed on version 71
  1665. if (UI.lastVersionCode <= 70 && UI.lastVersionCode != 0) {
  1666. final int volumeControlType = opts.getInt(OPT_VOLUMECONTROLTYPE, UI.isTV ? VOLUME_CONTROL_NONE : VOLUME_CONTROL_STREAM);
  1667. if (volumeControlType == VOLUME_CONTROL_DB)
  1668. setVolumeControlType((opts.hasBits() ? opts.getBit(OPTBIT_DISPLAYVOLUMEINDB) : opts.getBoolean(OPT_DISPLAYVOLUMEINDB)) ? VOLUME_CONTROL_DB : VOLUME_CONTROL_PERCENT);
  1669. else
  1670. setVolumeControlType(volumeControlType);
  1671. } else {
  1672. //load the volume control type the new way
  1673. final int defVolumeControlType = (UI.isTV ? VOLUME_CONTROL_NONE : VOLUME_CONTROL_STREAM);
  1674. setVolumeControlType(opts.getBitI(OPTBIT_VOLUMECONTROLTYPE0, defVolumeControlType & 1) |
  1675. (opts.getBitI(OPTBIT_VOLUMECONTROLTYPE1, defVolumeControlType >> 1) << 1));
  1676. }
  1677. //the concept of bit was added on version 38 (the old way was removed on version 74)
  1678. //if (opts.hasBits() || UI.lastVersionCode == 0) {
  1679. //load the bit flags the new way
  1680. controlMode = opts.getBit(OPTBIT_CONTROLMODE);
  1681. bassBoostMode = opts.getBit(OPTBIT_BASSBOOSTMODE);
  1682. nextPreparationEnabled = opts.getBit(OPTBIT_NEXTPREPARATION, true);
  1683. clearListWhenPlayingFolders = opts.getBit(OPTBIT_PLAYFOLDERCLEARSLIST);
  1684. UI.keepScreenOn = opts.getBit(OPTBIT_KEEPSCREENON, true);
  1685. UI.doubleClickMode = opts.getBit(OPTBIT_DOUBLECLICKMODE);
  1686. UI.marqueeTitle = opts.getBit(OPTBIT_MARQUEETITLE, true);
  1687. UI.setFlat((UI.lastVersionCode < 74) || opts.getBit(OPTBIT_FLAT, true));
  1688. UI.hasBorders = ((UI.lastVersionCode >= 74) && opts.getBit(OPTBIT_BORDERS, false));
  1689. UI.animationEnabled = ((UI.lastVersionCode < 76 && UI.deviceSupportsAnimations) || opts.getBit(OPTBIT_ANIMATIONS, UI.deviceSupportsAnimations));
  1690. UI.albumArt = opts.getBit(OPTBIT_ALBUMART, true);
  1691. UI.blockBackKey = opts.getBit(OPTBIT_BLOCKBACKKEY);
  1692. UI.isDividerVisible = opts.getBit(OPTBIT_ISDIVIDERVISIBLE, true);
  1693. UI.setVerticalMarginLarge(opts.getBit(OPTBIT_ISVERTICALMARGINLARGE, true)); //UI.isLargeScreen || !UI.isLowDpiScreen));
  1694. handleCallKey = opts.getBit(OPTBIT_HANDLECALLKEY, true);
  1695. playWhenHeadsetPlugged = opts.getBit(OPTBIT_PLAYWHENHEADSETPLUGGED, true);
  1696. UI.setUsingAlternateTypefaceAndForcedLocale(context, opts.getBit(OPTBIT_USEALTERNATETYPEFACE), opts.getInt(OPT_FORCEDLOCALE, UI.LOCALE_NONE));
  1697. goBackWhenPlayingFolders = opts.getBit(OPTBIT_GOBACKWHENPLAYINGFOLDERS);
  1698. UI.widgetTransparentBg = opts.getBit(OPTBIT_WIDGETTRANSPARENTBG);
  1699. UI.backKeyAlwaysReturnsToPlayerWhenBrowsing = opts.getBit(OPTBIT_BACKKEYALWAYSRETURNSTOPLAYERWHENBROWSING);
  1700. UI.wrapAroundList = opts.getBit(OPTBIT_WRAPAROUNDLIST);
  1701. UI.extraSpacing = opts.getBit(OPTBIT_EXTRASPACING, UI.isTV || (UI.screenWidth >= UI.dpToPxI(600)) || (UI.screenHeight >= UI.dpToPxI(600)));
  1702. //new settings (cannot be loaded the old way)
  1703. headsetHookDoublePressPauses = opts.getBit(OPTBIT_HEADSETHOOK_DOUBLE_PRESS_PAUSES);
  1704. doNotAttenuateVolume = opts.getBit(OPTBIT_DO_NOT_ATTENUATE_VOLUME);
  1705. UI.scrollBarToTheLeft = opts.getBit(OPTBIT_SCROLLBAR_TO_THE_LEFT);
  1706. UI.songListScrollBarType = (opts.getBitI(OPTBIT_SCROLLBAR_SONGLIST1, 0) << 1) | opts.getBitI(OPTBIT_SCROLLBAR_SONGLIST0, UI.isTV ? 0 : 1);
  1707. if (UI.songListScrollBarType == BgListView.SCROLLBAR_INDEXED)
  1708. UI.songListScrollBarType = BgListView.SCROLLBAR_LARGE;
  1709. UI.browserScrollBarType = (opts.getBitI(OPTBIT_SCROLLBAR_BROWSER1, UI.isTV ? 0 : 1) << 1) | opts.getBitI(OPTBIT_SCROLLBAR_BROWSER0, 0);
  1710. lastRadioSearchWasByGenre = opts.getBit(OPTBIT_LASTRADIOSEARCHWASBYGENRE, true);
  1711. UI.expandSeekBar = opts.getBit(OPTBIT_EXPANDSEEKBAR, true);
  1712. songs.setRepeatMode(opts.getBit(OPTBIT_REPEATONE) ? SongList.REPEAT_ONE : (opts.getBit(OPTBIT_REPEATNONE) ? SongList.REPEAT_NONE : SongList.REPEAT_ALL));
  1713. songs.setRandomMode(opts.getBit(OPTBIT_RANDOMMODE));
  1714. UI.notFullscreen = opts.getBit(OPTBIT_NOTFULLSCREEN);
  1715. UI.controlsToTheLeft = opts.getBit(OPTBIT_CONTROLS_TO_THE_LEFT);
  1716. UI.visualizerPortrait = opts.getBit(OPTBIT_VISUALIZER_PORTRAIT);
  1717. turnOffWhenPlaylistEnds = opts.getBit(OPTBIT_TURNOFFPLAYLIST);
  1718. /*} else {
  1719. //load bit flags the old way
  1720. controlMode = opts.getBoolean(OPT_CONTROLMODE);
  1721. bassBoostMode = opts.getBoolean(OPT_BASSBOOSTMODE);
  1722. nextPreparationEnabled = opts.getBoolean(OPT_NEXTPREPARATION, true);
  1723. clearListWhenPlayingFolders = opts.getBoolean(OPT_PLAYFOLDERCLEARSLIST);
  1724. UI.keepScreenOn = opts.getBoolean(OPT_KEEPSCREENON, true);
  1725. UI.doubleClickMode = opts.getBoolean(OPT_DOUBLECLICKMODE);
  1726. UI.marqueeTitle = opts.getBoolean(OPT_MARQUEETITLE, true);
  1727. UI.setFlat(opts.getBoolean(OPT_FLAT, true));
  1728. UI.albumArt = opts.getBoolean(OPT_ALBUMART, true);
  1729. UI.blockBackKey = opts.getBoolean(OPT_BLOCKBACKKEY);
  1730. UI.isDividerVisible = opts.getBoolean(OPT_ISDIVIDERVISIBLE, true);
  1731. UI.setVerticalMarginLarge(opts.getBoolean(OPT_ISVERTICALMARGINLARGE, true)); //UI.isLargeScreen || !UI.isLowDpiScreen));
  1732. handleCallKey = opts.getBoolean(OPT_HANDLECALLKEY, true);
  1733. playWhenHeadsetPlugged = opts.getBoolean(OPT_PLAYWHENHEADSETPLUGGED, true);
  1734. UI.setUsingAlternateTypefaceAndForcedLocale(context, opts.getBoolean(OPT_USEALTERNATETYPEFACE), opts.getInt(OPT_FORCEDLOCALE, UI.LOCALE_NONE));
  1735. goBackWhenPlayingFolders = opts.getBoolean(OPT_GOBACKWHENPLAYINGFOLDERS);
  1736. songs.setRandomMode(opts.getBoolean(OPT_RANDOMMODE));
  1737. UI.widgetTransparentBg = opts.getBoolean(OPT_WIDGETTRANSPARENTBG);
  1738. UI.backKeyAlwaysReturnsToPlayerWhenBrowsing = opts.getBoolean(OPT_BACKKEYALWAYSRETURNSTOPLAYERWHENBROWSING);
  1739. UI.wrapAroundList = opts.getBoolean(OPT_WRAPAROUNDLIST);
  1740. UI.extraSpacing = opts.getBoolean(OPT_EXTRASPACING, (UI.screenWidth >= UI.dpToPxI(600)) || (UI.screenHeight >= UI.dpToPxI(600)));
  1741. //Load default values for new settings
  1742. UI.songListScrollBarType = (UI.isTV ? BgListView.SCROLLBAR_SYSTEM : BgListView.SCROLLBAR_LARGE);
  1743. UI.browserScrollBarType = (UI.isTV ? BgListView.SCROLLBAR_SYSTEM : BgListView.SCROLLBAR_INDEXED);
  1744. lastRadioSearchWasByGenre = true;
  1745. UI.expandSeekBar = true;
  1746. }*/
  1747. int count = opts.getInt(OPT_FAVORITEFOLDERCOUNT);
  1748. if (count > 0) {
  1749. if (count > 128)
  1750. count = 128;
  1751. favoriteFolders.clear();
  1752. for (int i = count - 1; i >= 0; i--) {
  1753. final String f = opts.getString(OPT_FAVORITEFOLDER0 + i);
  1754. if (f != null && f.length() > 1)
  1755. favoriteFolders.add(f);
  1756. }
  1757. }
  1758. Equalizer.loadConfig(opts);
  1759. BassBoost.loadConfig(opts);
  1760. Virtualizer.loadConfig(opts);
  1761. //PresetReverb.loadConfig(opts);
  1762. }
  1763. public static void saveConfig(Context context, boolean saveSongs) {
  1764. final SerializableMap opts = new SerializableMap(96);
  1765. opts.put(OPT_LASTVERSIONCODE, UI.VERSION_CODE);
  1766. opts.put(OPT_VOLUME, volumeDB);
  1767. opts.put(OPT_PATH, path);
  1768. opts.put(OPT_ORIGINALPATH, originalPath);
  1769. opts.put(OPT_LASTTIME, storedSongTime);
  1770. opts.put(OPT_FADEININCREMENTONFOCUS, fadeInIncrementOnFocus);
  1771. opts.put(OPT_FADEININCREMENTONPAUSE, fadeInIncrementOnPause);
  1772. opts.put(OPT_FADEININCREMENTONOTHER, fadeInIncrementOnOther);
  1773. opts.put(OPT_FORCEDORIENTATION, UI.forcedOrientation);
  1774. opts.put(OPT_TURNOFFTIMERCUSTOMMINUTES, turnOffTimerCustomMinutes);
  1775. opts.put(OPT_TURNOFFTIMERSELECTEDMINUTES, turnOffTimerSelectedMinutes);
  1776. opts.put(OPT_IDLETURNOFFTIMERCUSTOMMINUTES, idleTurnOffTimerCustomMinutes);
  1777. opts.put(OPT_IDLETURNOFFTIMERSELECTEDMINUTES, idleTurnOffTimerSelectedMinutes);
  1778. opts.put(OPT_CUSTOMCOLORS, UI.customColors);
  1779. opts.put(OPT_THEME, UI.theme);
  1780. opts.put(OPT_FORCEDLOCALE, UI.forcedLocale);
  1781. opts.put(OPT_MSGS, UI.msgs);
  1782. opts.put(OPT_MSGSTARTUP, UI.msgStartup);
  1783. opts.put(OPT_WIDGETTEXTCOLOR, UI.widgetTextColor);
  1784. opts.put(OPT_WIDGETICONCOLOR, UI.widgetIconColor);
  1785. opts.put(OPT_BLUETOOTHVISUALIZERCONFIG, bluetoothVisualizerConfig);
  1786. opts.put(OPT_SONGEXTRAINFOMODE, Song.extraInfoMode);
  1787. opts.put(OPT_RADIOSEARCHTERM, radioSearchTerm);
  1788. opts.put(OPT_RADIOLASTGENRE, radioLastGenre);
  1789. opts.put(OPT_TRANSITION, UI.transition);
  1790. opts.putBit(OPTBIT_CONTROLMODE, controlMode);
  1791. opts.putBit(OPTBIT_BASSBOOSTMODE, bassBoostMode);
  1792. opts.putBit(OPTBIT_NEXTPREPARATION, nextPreparationEnabled);
  1793. opts.putBit(OPTBIT_PLAYFOLDERCLEARSLIST, clearListWhenPlayingFolders);
  1794. opts.putBit(OPTBIT_KEEPSCREENON, UI.keepScreenOn);
  1795. opts.putBit(OPTBIT_VOLUMECONTROLTYPE0, (volumeControlType & 1) != 0);
  1796. opts.putBit(OPTBIT_VOLUMECONTROLTYPE1, (volumeControlType & 2) != 0);
  1797. opts.putBit(OPTBIT_DOUBLECLICKMODE, UI.doubleClickMode);
  1798. opts.putBit(OPTBIT_MARQUEETITLE, UI.marqueeTitle);
  1799. opts.putBit(OPTBIT_FLAT, UI.isFlat);
  1800. opts.putBit(OPTBIT_BORDERS, UI.hasBorders);
  1801. opts.putBit(OPTBIT_ANIMATIONS, UI.animationEnabled);
  1802. opts.putBit(OPTBIT_ALBUMART, UI.albumArt);
  1803. opts.putBit(OPTBIT_BLOCKBACKKEY, UI.blockBackKey);
  1804. opts.putBit(OPTBIT_ISDIVIDERVISIBLE, UI.isDividerVisible);
  1805. opts.putBit(OPTBIT_ISVERTICALMARGINLARGE, UI.isVerticalMarginLarge);
  1806. opts.putBit(OPTBIT_HANDLECALLKEY, handleCallKey);
  1807. opts.putBit(OPTBIT_PLAYWHENHEADSETPLUGGED, playWhenHeadsetPlugged);
  1808. opts.putBit(OPTBIT_USEALTERNATETYPEFACE, UI.isUsingAlternateTypeface);
  1809. opts.putBit(OPTBIT_GOBACKWHENPLAYINGFOLDERS, goBackWhenPlayingFolders);
  1810. opts.putBit(OPTBIT_RANDOMMODE, songs.isInRandomMode());
  1811. opts.putBit(OPTBIT_WIDGETTRANSPARENTBG, UI.widgetTransparentBg);
  1812. opts.putBit(OPTBIT_BACKKEYALWAYSRETURNSTOPLAYERWHENBROWSING, UI.backKeyAlwaysReturnsToPlayerWhenBrowsing);
  1813. opts.putBit(OPTBIT_WRAPAROUNDLIST, UI.wrapAroundList);
  1814. opts.putBit(OPTBIT_EXTRASPACING, UI.extraSpacing);
  1815. opts.putBit(OPTBIT_HEADSETHOOK_DOUBLE_PRESS_PAUSES, headsetHookDoublePressPauses);
  1816. opts.putBit(OPTBIT_DO_NOT_ATTENUATE_VOLUME, doNotAttenuateVolume);
  1817. opts.putBit(OPTBIT_SCROLLBAR_TO_THE_LEFT, UI.scrollBarToTheLeft);
  1818. opts.putBit(OPTBIT_SCROLLBAR_SONGLIST0, (UI.songListScrollBarType & 1) != 0);
  1819. opts.putBit(OPTBIT_SCROLLBAR_SONGLIST1, (UI.songListScrollBarType & 2) != 0);
  1820. opts.putBit(OPTBIT_SCROLLBAR_BROWSER0, (UI.browserScrollBarType & 1) != 0);
  1821. opts.putBit(OPTBIT_SCROLLBAR_BROWSER1, (UI.browserScrollBarType & 2) != 0);
  1822. opts.putBit(OPTBIT_LASTRADIOSEARCHWASBYGENRE, lastRadioSearchWasByGenre);
  1823. opts.putBit(OPTBIT_EXPANDSEEKBAR, UI.expandSeekBar);
  1824. opts.putBit(OPTBIT_REPEATONE, songs.getRepeatMode() == SongList.REPEAT_ONE);
  1825. opts.putBit(OPTBIT_REPEATNONE, songs.getRepeatMode() == SongList.REPEAT_NONE);
  1826. opts.putBit(OPTBIT_NOTFULLSCREEN, UI.notFullscreen);
  1827. opts.putBit(OPTBIT_CONTROLS_TO_THE_LEFT, UI.controlsToTheLeft);
  1828. opts.putBit(OPTBIT_VISUALIZER_PORTRAIT, UI.visualizerPortrait);
  1829. opts.putBit(OPTBIT_TURNOFFPLAYLIST, turnOffWhenPlaylistEnds);
  1830. if (favoriteFolders.size() > 0) {
  1831. opts.put(OPT_FAVORITEFOLDERCOUNT, favoriteFolders.size());
  1832. int i = 0;
  1833. for (String f : favoriteFolders) {
  1834. opts.put(OPT_FAVORITEFOLDER0 + i, f);
  1835. i++;
  1836. }
  1837. } else {
  1838. opts.put(OPT_FAVORITEFOLDERCOUNT, 0);
  1839. }
  1840. Equalizer.saveConfig(opts);
  1841. BassBoost.saveConfig(opts);
  1842. Virtualizer.saveConfig(opts);
  1843. //PresetReverb.saveConfig(opts);
  1844. opts.serialize(context, "_Player");
  1845. if (saveSongs)
  1846. songs.serialize(context, null);
  1847. }
  1848. private static void createIntents(Context context) {
  1849. if (intentActivityHost == null) {
  1850. Intent intent = new Intent(context, ActivityHost.class);
  1851. intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
  1852. intentActivityHost = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
  1853. intent = new Intent(context, Player.class);
  1854. intent.setAction(Player.ACTION_PREVIOUS);
  1855. intentPrevious = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
  1856. intent = new Intent(context, Player.class);
  1857. intent.setAction(Player.ACTION_PLAY_PAUSE);
  1858. intentPlayPause = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
  1859. intent = new Intent(context, Player.class);
  1860. intent.setAction(Player.ACTION_NEXT);
  1861. intentNext = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
  1862. intent = new Intent(context, Player.class);
  1863. intent.setAction(Player.ACTION_EXIT);
  1864. intentExit = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
  1865. }
  1866. }
  1867. public static RemoteViews prepareRemoteViews(Context context, RemoteViews views, boolean prepareButtons, boolean notification, boolean notificationFirstTime) {
  1868. createIntents(context);
  1869. if (localSong == null)
  1870. views.setTextViewText(R.id.lblTitle, context.getText(R.string.nothing_playing));
  1871. else if (isPreparing())
  1872. views.setTextViewText(R.id.lblTitle, context.getText(R.string.loading));
  1873. else
  1874. views.setTextViewText(R.id.lblTitle, localSong.title);
  1875. if (prepareButtons) {
  1876. if (notification) {
  1877. UI.prepareNotificationPlaybackIcons(context);
  1878. views.setImageViewBitmap(R.id.btnPlay, playing ? UI.icPauseNotif : UI.icPlayNotif);
  1879. if (notificationFirstTime) {
  1880. views.setImageViewBitmap(R.id.btnPrev, UI.icPrevNotif);
  1881. views.setImageViewBitmap(R.id.btnNext, UI.icNextNotif);
  1882. views.setImageViewBitmap(R.id.btnExit, UI.icExitNotif);
  1883. views.setOnClickPendingIntent(R.id.btnPrev, intentPrevious);
  1884. views.setOnClickPendingIntent(R.id.btnPlay, intentPlayPause);
  1885. views.setOnClickPendingIntent(R.id.btnNext, intentNext);
  1886. views.setOnClickPendingIntent(R.id.btnExit, intentExit);
  1887. }
  1888. } else {
  1889. if (localSong == null)
  1890. views.setTextViewText(R.id.lblArtist, "-");
  1891. else
  1892. views.setTextViewText(R.id.lblArtist, localSong.extraInfo);
  1893. views.setTextColor(R.id.lblTitle, UI.widgetTextColor);
  1894. views.setTextColor(R.id.lblArtist, UI.widgetTextColor);
  1895. views.setOnClickPendingIntent(R.id.lblTitle, intentActivityHost);
  1896. views.setOnClickPendingIntent(R.id.lblArtist, intentActivityHost);
  1897. UI.prepareWidgetPlaybackIcons(context);
  1898. views.setImageViewBitmap(R.id.btnPrev, UI.icPrev);
  1899. views.setImageViewBitmap(R.id.btnPlay, playing ? UI.icPause : UI.icPlay);
  1900. views.setImageViewBitmap(R.id.btnNext, UI.icNext);
  1901. views.setOnClickPendingIntent(R.id.btnPrev, intentPrevious);
  1902. views.setOnClickPendingIntent(R.id.btnPlay, intentPlayPause);
  1903. views.setOnClickPendingIntent(R.id.btnNext, intentNext);
  1904. }
  1905. }
  1906. return views;
  1907. }
  1908. @SuppressWarnings("deprecation")
  1909. private static Notification getNotification() {
  1910. boolean firstTime = false;
  1911. if (notification == null) {
  1912. firstTime = true;
  1913. notification = new Notification();
  1914. notification.icon = R.drawable.ic_notification;
  1915. notification.when = 0;
  1916. notification.flags = Notification.FLAG_FOREGROUND_SERVICE;
  1917. notification.contentIntent = intentActivityHost;
  1918. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
  1919. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  1920. //any need for this technique???
  1921. //https://developer.android.com/about/versions/android-5.0-changes.html#BehaviorMediaControl
  1922. //https://developer.android.com/reference/android/app/Notification.MediaStyle.html
  1923. notification.visibility = Notification.VISIBILITY_PUBLIC;
  1924. if (mediaSession != null)
  1925. notification.extras.putParcelable(Notification.EXTRA_MEDIA_SESSION, mediaSession.getSessionToken());
  1926. }
  1927. notificationRemoteViews = new RemoteViews(thePlayer.getPackageName(), R.layout.notification);
  1928. } else {
  1929. notificationRemoteViews = new RemoteViews(thePlayer.getPackageName(), R.layout.notification_simple);
  1930. }
  1931. notification.contentView = notificationRemoteViews;
  1932. }
  1933. prepareRemoteViews(thePlayer, notificationRemoteViews, Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN, true, firstTime);
  1934. return notification;
  1935. }
  1936. public static void setControlMode(boolean controlMode) {
  1937. Player.controlMode = controlMode;
  1938. if (observer != null)
  1939. observer.onPlayerControlModeChanged(controlMode);
  1940. }
  1941. @Override
  1942. public int compare(FileSt a, FileSt b) {
  1943. return a.name.compareToIgnoreCase(b.name);
  1944. }
  1945. public static void addFavoriteFolder(String path) {
  1946. synchronized (favoriteFolders) {
  1947. favoriteFolders.add(path);
  1948. }
  1949. }
  1950. public static void removeFavoriteFolder(String path) {
  1951. synchronized (favoriteFolders) {
  1952. favoriteFolders.remove(path);
  1953. }
  1954. }
  1955. public static boolean isFavoriteFolder(String path) {
  1956. synchronized (favoriteFolders) {
  1957. return favoriteFolders.contains(path);
  1958. }
  1959. }
  1960. public static FileSt[] getFavoriteFolders(int extra) {
  1961. FileSt[] ffs;
  1962. synchronized (favoriteFolders) {
  1963. final int count = favoriteFolders.size();
  1964. if (count == 0)
  1965. return new FileSt[extra];
  1966. ffs = new FileSt[count + extra];
  1967. int i = 0;
  1968. for (String f : favoriteFolders) {
  1969. final int idx = f.lastIndexOf('/');
  1970. ffs[i] = new FileSt(f, (idx >= 0 && idx < (f.length() - 1)) ? f.substring(idx + 1) : f, null, FileSt.TYPE_FAVORITE);
  1971. i++;
  1972. }
  1973. }
  1974. ArraySorter.sort(ffs, 0, ffs.length - extra, thePlayer);
  1975. return ffs;
  1976. }
  1977. public static void setSelectionAfterAdding(int positionToSelect) {
  1978. if (!alreadySelected) {
  1979. alreadySelected = true;
  1980. if (!songs.selecting && !songs.moving)
  1981. songs.setSelection(positionToSelect, false);
  1982. if (!isMainActiveOnTop)
  1983. Player.positionToSelect = positionToSelect;
  1984. }
  1985. }
  1986. public static void songListDeserialized(Song newCurrentSong, int forcePlayIdx, int positionToSelect, Throwable ex) {
  1987. if (positionToSelect >= 0)
  1988. setSelectionAfterAdding(positionToSelect);
  1989. if (newCurrentSong != null)
  1990. localSong = newCurrentSong;
  1991. if (handler != null) {
  1992. if (newCurrentSong != null)
  1993. handler.sendMessageAtTime(Message.obtain(handler, MSG_SONG_LIST_DESERIALIZED, newCurrentSong), SystemClock.uptimeMillis());
  1994. if (ex != null)
  1995. handler.sendMessageAtTime(Message.obtain(handler, MSG_SONG_LIST_DESERIALIZED, ex), SystemClock.uptimeMillis());
  1996. }
  1997. switch (state) {
  1998. case STATE_INITIALIZING:
  1999. case STATE_INITIALIZING_STEP2:
  2000. localHandler.sendEmptyMessageAtTime(MSG_INITIALIZATION_STEP, SystemClock.uptimeMillis());
  2001. break;
  2002. case STATE_ALIVE:
  2003. if (ex == null && forcePlayIdx >= 0)
  2004. play(forcePlayIdx);
  2005. break;
  2006. }
  2007. }
  2008. public static boolean isConnectedToTheInternet() {
  2009. if (thePlayer != null) {
  2010. try {
  2011. final ConnectivityManager mngr = (ConnectivityManager)thePlayer.getSystemService(Context.CONNECTIVITY_SERVICE);
  2012. final NetworkInfo info = mngr.getActiveNetworkInfo();
  2013. return (info != null && info.isConnected());
  2014. } catch (Throwable ex) {
  2015. ex.printStackTrace();
  2016. }
  2017. }
  2018. return false;
  2019. }
  2020. @SuppressWarnings({"deprecation", "unused"})
  2021. public static boolean isInternetConnectedViaWiFi() {
  2022. if (thePlayer != null) {
  2023. try {
  2024. final ConnectivityManager mngr = (ConnectivityManager)thePlayer.getSystemService(Context.CONNECTIVITY_SERVICE);
  2025. final NetworkInfo infoMob = mngr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
  2026. final NetworkInfo info = mngr.getActiveNetworkInfo();
  2027. return (infoMob != null && info != null && !infoMob.isConnected() && info.isConnected());
  2028. } catch (Throwable ex) {
  2029. ex.printStackTrace();
  2030. }
  2031. }
  2032. return false;
  2033. }
  2034. private static void processTurnOffTimer() {
  2035. if (state > STATE_ALIVE)
  2036. return;
  2037. localHandler.removeMessages(MSG_TURN_OFF_TIMER);
  2038. if (turnOffTimerOrigin > 0) {
  2039. final int secondsLeft = (turnOffTimerSelectedMinutes * 60) - (int)((SystemClock.elapsedRealtime() - turnOffTimerOrigin) / 1000);
  2040. if (turnOffTimerObserver != null)
  2041. turnOffTimerObserver.onPlayerTurnOffTimerTick();
  2042. if (secondsLeft < 15) //less than half of our period
  2043. stopService(false);
  2044. else
  2045. localHandler.sendEmptyMessageAtTime(MSG_TURN_OFF_TIMER, SystemClock.uptimeMillis() + 30000);
  2046. }
  2047. }
  2048. public static void setTurnOffTimer(int minutes) {
  2049. if (state > STATE_ALIVE)
  2050. return;
  2051. if (minutes > 0) {
  2052. if (minutes != 60 && minutes != 90 && minutes != 120)
  2053. turnOffTimerCustomMinutes = minutes;
  2054. //we must use SystemClock.elapsedRealtime because uptimeMillis
  2055. //does not take sleep time into account (and the user makes no
  2056. //difference between the time spent during sleep and the one
  2057. //while actually working)
  2058. turnOffTimerOrigin = SystemClock.elapsedRealtime();
  2059. turnOffTimerSelectedMinutes = minutes;
  2060. } else {
  2061. turnOffTimerOrigin = 0;
  2062. turnOffTimerSelectedMinutes = 0;
  2063. }
  2064. processTurnOffTimer();
  2065. }
  2066. public static int getTurnOffTimerMinutesLeft() {
  2067. if (turnOffTimerOrigin <= 0)
  2068. return turnOffTimerSelectedMinutes;
  2069. final int m = turnOffTimerSelectedMinutes - (int)((SystemClock.elapsedRealtime() - turnOffTimerOrigin) / 60000L);
  2070. return ((m <= 0) ? 1 : m);
  2071. }
  2072. public static void setAppNotInForeground(boolean appNotInForeground) {
  2073. if (state > STATE_ALIVE)
  2074. return;
  2075. if (Player.appNotInForeground != appNotInForeground) {
  2076. Player.appNotInForeground = appNotInForeground;
  2077. if (idleTurnOffTimerSelectedMinutes > 0)
  2078. processIdleTurnOffTimer();
  2079. }
  2080. }
  2081. private static void processIdleTurnOffTimer() {
  2082. if (state > STATE_ALIVE)
  2083. return;
  2084. if (idleTurnOffTimerSelectedMinutes <= 0) {
  2085. idleTurnOffTimerSent = false;
  2086. localHandler.removeMessages(MSG_IDLE_TURN_OFF_TIMER);
  2087. return;
  2088. }
  2089. boolean wasPlayingBeforeOngoingCall = false, sendMessage = false;
  2090. final boolean idle = (!playing && appNotInForeground);
  2091. if (idle && telephonyManager != null) {
  2092. //check for ongoing call
  2093. try {
  2094. if (telephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE)
  2095. wasPlayingBeforeOngoingCall = resumePlaybackAfterFocusGain;
  2096. } catch (Throwable ex) {
  2097. ex.printStackTrace();
  2098. }
  2099. }
  2100. if (!idle || wasPlayingBeforeOngoingCall) {
  2101. if (idle) {
  2102. //consider time spent in calls as active time, but keep checking,
  2103. //because when the call ends, the audio focus could go to someone
  2104. //else, rendering us actually idle!
  2105. idleTurnOffTimerOrigin = SystemClock.elapsedRealtime();
  2106. sendMessage = true;
  2107. } else {
  2108. idleTurnOffTimerOrigin = 0; //it's safe to reset the origin
  2109. }
  2110. if (turnOffTimerObserver != null)
  2111. turnOffTimerObserver.onPlayerIdleTurnOffTimerTick();
  2112. } else {
  2113. if (idleTurnOffTimerOrigin > 0) {
  2114. final int secondsLeft = (idleTurnOffTimerSelectedMinutes * 60) - (int)((SystemClock.elapsedRealtime() - idleTurnOffTimerOrigin) / 1000);
  2115. if (turnOffTimerObserver != null)
  2116. turnOffTimerObserver.onPlayerIdleTurnOffTimerTick();
  2117. if (secondsLeft < 15) //less than half of our period
  2118. stopService(false);
  2119. else
  2120. sendMessage = true;
  2121. } else {
  2122. idleTurnOffTimerOrigin = SystemClock.elapsedRealtime();
  2123. sendMessage = true;
  2124. }
  2125. }
  2126. if (sendMessage) {
  2127. if (!idleTurnOffTimerSent) {
  2128. idleTurnOffTimerSent = true;
  2129. localHandler.sendEmptyMessageAtTime(MSG_IDLE_TURN_OFF_TIMER, SystemClock.uptimeMillis() + 30000);
  2130. }
  2131. } else {
  2132. if (idleTurnOffTimerSent) {
  2133. idleTurnOffTimerSent = false;
  2134. localHandler.removeMessages(MSG_IDLE_TURN_OFF_TIMER);
  2135. }
  2136. }
  2137. }
  2138. public static void setIdleTurnOffTimer(int minutes) {
  2139. if (state > STATE_ALIVE)
  2140. return;
  2141. idleTurnOffTimerOrigin = 0;
  2142. idleTurnOffTimerSent = false;
  2143. localHandler.removeMessages(MSG_IDLE_TURN_OFF_TIMER);
  2144. if (minutes > 0) {
  2145. if (minutes != 60 && minutes != 90 && minutes != 120)
  2146. idleTurnOffTimerCustomMinutes = minutes;
  2147. idleTurnOffTimerSelectedMinutes = minutes;
  2148. processIdleTurnOffTimer();
  2149. } else {
  2150. idleTurnOffTimerSelectedMinutes = 0;
  2151. }
  2152. }
  2153. public static int getIdleTurnOffTimerMinutesLeft() {
  2154. if (idleTurnOffTimerOrigin <= 0)
  2155. return idleTurnOffTimerSelectedMinutes;
  2156. final int m = idleTurnOffTimerSelectedMinutes - (int)((SystemClock.elapsedRealtime() - idleTurnOffTimerOrigin) / 60000L);
  2157. return ((m <= 0) ? 1 : m);
  2158. }
  2159. private static void _songListDeserialized(Object obj) {
  2160. if (obj instanceof Throwable) {
  2161. _updateState(false, (Throwable)obj);
  2162. } else {
  2163. song = (Song)obj;
  2164. _updateState(false, null);
  2165. }
  2166. }
  2167. private static Intent stickyBroadcast;
  2168. private static ExternalReceiver externalReceiver;
  2169. private static ComponentName mediaButtonEventReceiver;
  2170. @SuppressWarnings("deprecation")
  2171. private static RemoteControlClient remoteControlClient;
  2172. private static MediaSession mediaSession;
  2173. private static MediaMetadata.Builder mediaSessionMetadataBuilder;
  2174. private static PlaybackState.Builder mediaSessionPlaybackStateBuilder;
  2175. private static Object mediaRouterCallback;
  2176. private static ArrayList<PlayerDestroyedObserver> destroyedObservers;
  2177. public static PlayerTurnOffTimerObserver turnOffTimerObserver;
  2178. public static PlayerObserver observer;
  2179. @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
  2180. private static void registerMediaRouter(Context context) {
  2181. final MediaRouter mr = (MediaRouter)context.getSystemService(MEDIA_ROUTER_SERVICE);
  2182. if (mr != null) {
  2183. mediaRouterCallback = new MediaRouter.Callback() {
  2184. @Override
  2185. public void onRouteVolumeChanged(MediaRouter router, MediaRouter.RouteInfo info) {
  2186. }
  2187. @Override
  2188. public void onRouteUnselected(MediaRouter router, int type, MediaRouter.RouteInfo info) {
  2189. }
  2190. @Override
  2191. public void onRouteUngrouped(MediaRouter router, MediaRouter.RouteInfo info, MediaRouter.RouteGroup group) {
  2192. }
  2193. @Override
  2194. public void onRouteSelected(MediaRouter router, int type, MediaRouter.RouteInfo info) {
  2195. }
  2196. @Override
  2197. public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) {
  2198. }
  2199. @Override
  2200. public void onRouteGrouped(MediaRouter router, MediaRouter.RouteInfo info, MediaRouter.RouteGroup group, int index) {
  2201. }
  2202. @Override
  2203. public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) {
  2204. if (info.getPlaybackStream() == AudioManager.STREAM_MUSIC) {
  2205. //this actually works... nonetheless, I was not able to detected
  2206. //which is the audio sink used by this route.... :(
  2207. volumeStreamMax = info.getVolumeMax();
  2208. if (volumeStreamMax < 1)
  2209. volumeStreamMax = 1;
  2210. audioSinkChanged(false);
  2211. }
  2212. }
  2213. @Override
  2214. public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) {
  2215. }
  2216. };
  2217. mr.addCallback(MediaRouter.ROUTE_TYPE_LIVE_AUDIO | MediaRouter.ROUTE_TYPE_USER, (MediaRouter.Callback)mediaRouterCallback);
  2218. }
  2219. }
  2220. @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
  2221. private static void unregisterMediaRouter(Context context) {
  2222. final MediaRouter mr = (MediaRouter)context.getSystemService(MEDIA_ROUTER_SERVICE);
  2223. if (mediaRouterCallback != null && mr != null)
  2224. mr.removeCallback((MediaRouter.Callback)mediaRouterCallback);
  2225. mediaRouterCallback = null;
  2226. }
  2227. @SuppressWarnings("deprecation")
  2228. @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
  2229. private static void registerRemoteControlClientCallbacks() {
  2230. remoteControlClient.setOnGetPlaybackPositionListener(new RemoteControlClient.OnGetPlaybackPositionListener() {
  2231. @Override
  2232. public long onGetPlaybackPosition() {
  2233. return getPosition();
  2234. }
  2235. });
  2236. remoteControlClient.setPlaybackPositionUpdateListener(new RemoteControlClient.OnPlaybackPositionUpdateListener() {
  2237. @Override
  2238. public void onPlaybackPositionUpdate(long pos) {
  2239. seekTo((int)pos);
  2240. }
  2241. });
  2242. }
  2243. @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
  2244. private static void unregisterRemoteControlClientCallbacks() {
  2245. remoteControlClient.setOnGetPlaybackPositionListener(null);
  2246. remoteControlClient.setPlaybackPositionUpdateListener(null);
  2247. }
  2248. @SuppressWarnings("deprecation")
  2249. @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
  2250. private static void registerRemoteControlClient() {
  2251. try {
  2252. if (remoteControlClient == null) {
  2253. final Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
  2254. mediaButtonIntent.setComponent(mediaButtonEventReceiver);
  2255. final PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(thePlayer, 0, mediaButtonIntent, 0);
  2256. remoteControlClient = new RemoteControlClient(mediaPendingIntent);
  2257. remoteControlClient.setTransportControlFlags(RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS | RemoteControlClient.FLAG_KEY_MEDIA_NEXT | RemoteControlClient.FLAG_KEY_MEDIA_PLAY | RemoteControlClient.FLAG_KEY_MEDIA_PAUSE | RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE | RemoteControlClient.FLAG_KEY_MEDIA_STOP);
  2258. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
  2259. registerRemoteControlClientCallbacks();
  2260. }
  2261. audioManager.registerRemoteControlClient(remoteControlClient);
  2262. } catch (Throwable ex) {
  2263. ex.printStackTrace();
  2264. }
  2265. }
  2266. @SuppressWarnings("deprecation")
  2267. @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
  2268. private static void unregisterRemoteControlClient() {
  2269. try {
  2270. if (remoteControlClient != null) {
  2271. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
  2272. unregisterRemoteControlClientCallbacks();
  2273. audioManager.unregisterRemoteControlClient(remoteControlClient);
  2274. remoteControlClient = null;
  2275. }
  2276. } catch (Throwable ex) {
  2277. ex.printStackTrace();
  2278. }
  2279. }
  2280. @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  2281. private static void registerMediaSession() {
  2282. try {
  2283. if (mediaSession == null && thePlayer != null) {
  2284. mediaSessionMetadataBuilder = new MediaMetadata.Builder();
  2285. mediaSessionPlaybackStateBuilder = new PlaybackState.Builder();
  2286. mediaSessionPlaybackStateBuilder.setActions(PlaybackState.ACTION_SKIP_TO_PREVIOUS | PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY_PAUSE | PlaybackState.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_STOP | PlaybackState.ACTION_SEEK_TO);
  2287. mediaSession = new MediaSession(thePlayer, "FPlay");
  2288. mediaSession.setCallback(new MediaSession.Callback() {
  2289. @Override
  2290. public boolean onMediaButtonEvent(Intent mediaButtonIntent) {
  2291. if (mediaButtonIntent != null) {
  2292. final Object o = mediaButtonIntent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
  2293. if (o == null || !(o instanceof KeyEvent))
  2294. return false;
  2295. final KeyEvent e = (KeyEvent)o;
  2296. if (e.getAction() == KeyEvent.ACTION_DOWN)
  2297. handleMediaButton(e.getKeyCode());
  2298. }
  2299. return true;
  2300. }
  2301. @Override
  2302. public void onPlay() {
  2303. handleMediaButton(KeyEvent.KEYCODE_MEDIA_PLAY);
  2304. }
  2305. @Override
  2306. public void onPause() {
  2307. handleMediaButton(KeyEvent.KEYCODE_MEDIA_PAUSE);
  2308. }
  2309. @Override
  2310. public void onSkipToNext() {
  2311. handleMediaButton(KeyEvent.KEYCODE_MEDIA_NEXT);
  2312. }
  2313. @Override
  2314. public void onSkipToPrevious() {
  2315. handleMediaButton(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
  2316. }
  2317. @Override
  2318. public void onStop() {
  2319. handleMediaButton(KeyEvent.KEYCODE_MEDIA_STOP);
  2320. }
  2321. @Override
  2322. public void onSeekTo(long pos) {
  2323. seekTo((int)pos);
  2324. }
  2325. });
  2326. mediaSession.setSessionActivity(intentActivityHost);
  2327. mediaSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
  2328. mediaSession.setPlaybackState(mediaSessionPlaybackStateBuilder.setState(PlaybackState.STATE_STOPPED, 0, 1, SystemClock.elapsedRealtime()).build());
  2329. }
  2330. if (mediaSession != null)
  2331. mediaSession.setActive(true);
  2332. } catch (Throwable ex) {
  2333. ex.printStackTrace();
  2334. }
  2335. }
  2336. @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  2337. private static void unregisterMediaSession() {
  2338. try {
  2339. if (mediaSession != null) {
  2340. mediaSession.setActive(false);
  2341. mediaSession.setCallback(null);
  2342. mediaSession.release();
  2343. mediaSession = null;
  2344. }
  2345. mediaSessionMetadataBuilder = null;
  2346. mediaSessionPlaybackStateBuilder = null;
  2347. } catch (Throwable ex) {
  2348. ex.printStackTrace();
  2349. }
  2350. }
  2351. @SuppressWarnings("deprecation")
  2352. public static void registerMediaButtonEventReceiver() {
  2353. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  2354. registerMediaSession();
  2355. } else {
  2356. if (mediaButtonEventReceiver == null)
  2357. mediaButtonEventReceiver = new ComponentName("br.com.carlosrafaelgn.fplay", "br.com.carlosrafaelgn.fplay.ExternalReceiver");
  2358. if (audioManager != null) {
  2359. audioManager.registerMediaButtonEventReceiver(mediaButtonEventReceiver);
  2360. if (thePlayer != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
  2361. registerRemoteControlClient();
  2362. }
  2363. }
  2364. }
  2365. @SuppressWarnings("deprecation")
  2366. public static void unregisterMediaButtonEventReceiver() {
  2367. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  2368. unregisterMediaSession();
  2369. } else {
  2370. if (mediaButtonEventReceiver != null && audioManager != null) {
  2371. if (remoteControlClient != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
  2372. unregisterRemoteControlClient();
  2373. audioManager.unregisterMediaButtonEventReceiver(mediaButtonEventReceiver);
  2374. }
  2375. mediaButtonEventReceiver = null;
  2376. }
  2377. }
  2378. public static void becomingNoisy() {
  2379. if (handler != null)
  2380. handler.sendEmptyMessageAtTime(MSG_BECOMING_NOISY, SystemClock.uptimeMillis());
  2381. }
  2382. public static void audioSinkChanged(boolean wiredHeadsetJustPlugged) {
  2383. if (handler != null)
  2384. handler.sendMessageAtTime(Message.obtain(handler, MSG_AUDIO_SINK_CHANGED, wiredHeadsetJustPlugged ? 1 : 0, 0), SystemClock.uptimeMillis());
  2385. }
  2386. private static void processHeadsetHookTimer() {
  2387. if (state != STATE_ALIVE)
  2388. return;
  2389. if (headsetHookLastTime != 0) {
  2390. headsetHookLastTime = 0;
  2391. if (headsetHookDoublePressPauses)
  2392. next();
  2393. else
  2394. playPause();
  2395. }
  2396. }
  2397. public static boolean isMediaButton(int keyCode) {
  2398. switch (keyCode) {
  2399. case KeyEvent.KEYCODE_MEDIA_PLAY:
  2400. case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
  2401. case KeyEvent.KEYCODE_MEDIA_PAUSE:
  2402. case KeyEvent.KEYCODE_BREAK:
  2403. case KeyEvent.KEYCODE_HEADSETHOOK:
  2404. case KeyEvent.KEYCODE_MEDIA_STOP:
  2405. case KeyEvent.KEYCODE_CALL:
  2406. case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
  2407. case KeyEvent.KEYCODE_MEDIA_NEXT:
  2408. case KeyEvent.KEYCODE_MEDIA_REWIND:
  2409. case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
  2410. case KeyEvent.KEYCODE_VOLUME_DOWN:
  2411. case KeyEvent.KEYCODE_VOLUME_UP:
  2412. return true;
  2413. }
  2414. return false;
  2415. }
  2416. public static boolean handleMediaButton(int keyCode) {
  2417. switch (keyCode) {
  2418. //There are a few weird bluetooth headsets that despite having only one physical
  2419. //play/pause button, will try to simulate individual PLAY and PAUSE events,
  2420. //instead of sending the proper event PLAY_PAUSE... The problem is, they are not
  2421. //always synchronized with the actual player state, and therefore, the user ends
  2422. //up having to press that button twice for something to happen! :(
  2423. case KeyEvent.KEYCODE_MEDIA_PLAY:
  2424. case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
  2425. case KeyEvent.KEYCODE_MEDIA_PAUSE:
  2426. case KeyEvent.KEYCODE_BREAK:
  2427. playPause();
  2428. break;
  2429. case KeyEvent.KEYCODE_HEADSETHOOK:
  2430. if (localHandler != null) {
  2431. if (headsetHookLastTime != 0) {
  2432. localHandler.removeMessages(MSG_HEADSET_HOOK_TIMER);
  2433. if ((SystemClock.uptimeMillis() - headsetHookLastTime) < 500) {
  2434. headsetHookLastTime = 0;
  2435. if (headsetHookDoublePressPauses)
  2436. playPause();
  2437. else
  2438. next();
  2439. }
  2440. } else {
  2441. headsetHookLastTime = SystemClock.uptimeMillis();
  2442. localHandler.sendEmptyMessageAtTime(MSG_HEADSET_HOOK_TIMER, headsetHookLastTime + 500);
  2443. }
  2444. }
  2445. break;
  2446. case KeyEvent.KEYCODE_MEDIA_STOP:
  2447. pause();
  2448. break;
  2449. case KeyEvent.KEYCODE_CALL:
  2450. if (!handleCallKey)
  2451. return false;
  2452. playPause();
  2453. break;
  2454. case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
  2455. case KeyEvent.KEYCODE_MEDIA_NEXT:
  2456. next();
  2457. if (observer != null)
  2458. observer.onPlayerMediaButtonNext();
  2459. break;
  2460. case KeyEvent.KEYCODE_MEDIA_REWIND:
  2461. case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
  2462. previous();
  2463. if (observer != null)
  2464. observer.onPlayerMediaButtonPrevious();
  2465. break;
  2466. case KeyEvent.KEYCODE_VOLUME_DOWN:
  2467. if (volumeControlType == VOLUME_CONTROL_STREAM) {
  2468. keyCode = decreaseVolume();
  2469. if (observer != null)
  2470. observer.onPlayerGlobalVolumeChanged(keyCode);
  2471. break;
  2472. }
  2473. break;
  2474. case KeyEvent.KEYCODE_VOLUME_UP:
  2475. if (volumeControlType == VOLUME_CONTROL_STREAM) {
  2476. keyCode = increaseVolume();
  2477. if (observer != null)
  2478. observer.onPlayerGlobalVolumeChanged(keyCode);
  2479. break;
  2480. }
  2481. break;
  2482. default:
  2483. return false;
  2484. }
  2485. return true;
  2486. }
  2487. public static void listCleared() {
  2488. if (state != STATE_ALIVE)
  2489. return;
  2490. handler.sendEmptyMessageAtTime(MSG_LIST_CLEARED, SystemClock.uptimeMillis());
  2491. }
  2492. public static void nextMayHaveChanged(Song possibleNextSong) {
  2493. if (state != STATE_ALIVE)
  2494. return;
  2495. handler.sendMessageAtTime(Message.obtain(handler, MSG_NEXT_MAY_HAVE_CHANGED, possibleNextSong), SystemClock.uptimeMillis());
  2496. }
  2497. public static void addDestroyedObserver(PlayerDestroyedObserver observer) {
  2498. if (destroyedObservers != null && !destroyedObservers.contains(observer))
  2499. destroyedObservers.add(observer);
  2500. }
  2501. public static void removeDestroyedObserver(PlayerDestroyedObserver observer) {
  2502. if (destroyedObservers != null)
  2503. destroyedObservers.remove(observer);
  2504. }
  2505. @SuppressWarnings("deprecation")
  2506. private static void _checkAudioSink(boolean wiredHeadsetJustPlugged, boolean triggerNoisy) {
  2507. if (audioManager == null)
  2508. return;
  2509. final int oldAudioSink = audioSink;
  2510. //let the guessing begin!!! really, it is NOT possible to rely solely on
  2511. //these AudioManager.isXXX() methods, neither on MediaRouter...
  2512. //I would really be happy if things were as easy as the doc says... :(
  2513. //https://developer.android.com/training/managing-audio/audio-output.html
  2514. audioSink = 0;
  2515. try {
  2516. //isSpeakerphoneOn() has not actually returned true on any devices
  2517. //I have tested so far, anyway.... leaving this here won't hurt...
  2518. if (audioManager.isSpeakerphoneOn())
  2519. audioSink = AUDIO_SINK_DEVICE;
  2520. } catch (Throwable ex) {
  2521. ex.printStackTrace();
  2522. }
  2523. try {
  2524. //apparently, devices tend to use the wired headset over bluetooth headsets
  2525. if (audioSink == 0 && (wiredHeadsetJustPlugged || audioManager.isWiredHeadsetOn()))
  2526. audioSink = AUDIO_SINK_WIRE;
  2527. } catch (Throwable ex) {
  2528. ex.printStackTrace();
  2529. }
  2530. try {
  2531. //this whole A2dp thing is still not enough, as isA2dpPlaying()
  2532. //will return false if there is nothing playing, even in scenarios
  2533. //A2dp will certainly be used for playback later...
  2534. /*if (audioManager.isBluetoothA2dpOn()) {
  2535. //the device being on is not enough! we must be sure
  2536. //if it is actually being used for transmission...
  2537. if (thePlayer == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
  2538. if (audioSink == 0)
  2539. audioSink = AUDIO_SINK_BT;
  2540. } else {
  2541. if (audioSink == 0 || isA2dpPlaying())
  2542. audioSink = AUDIO_SINK_BT;
  2543. }
  2544. }*/
  2545. if (audioSink == 0 && audioManager.isBluetoothA2dpOn())
  2546. audioSink = AUDIO_SINK_BT;
  2547. } catch (Throwable ex) {
  2548. ex.printStackTrace();
  2549. }
  2550. if (audioSink == 0)
  2551. audioSink = AUDIO_SINK_DEVICE;
  2552. if (oldAudioSink != audioSink && oldAudioSink != 0) {
  2553. switch (audioSink) {
  2554. case AUDIO_SINK_WIRE:
  2555. if (!playing && playWhenHeadsetPlugged) {
  2556. if (!hasFocus) {
  2557. try {
  2558. if (telephonyManager != null && telephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE)
  2559. break;
  2560. } catch (Throwable ex) {
  2561. ex.printStackTrace();
  2562. }
  2563. }
  2564. _playPause();
  2565. }
  2566. break;
  2567. case AUDIO_SINK_DEVICE:
  2568. if (triggerNoisy) {
  2569. //this cleanup must be done, as sometimes, when changing between two output types,
  2570. //the effects are lost...
  2571. if (playing)
  2572. _playPause();
  2573. _fullCleanup();
  2574. }
  2575. break;
  2576. }
  2577. }
  2578. //I am calling the observer even if no changes have been detected, because
  2579. //I myself don't trust this code will correctly work as expected on every device....
  2580. if (localHandler != null)
  2581. localHandler.sendMessageAtTime(Message.obtain(localHandler, MSG_AUDIO_SINK_CHANGED, audioSink, 0), SystemClock.uptimeMillis());
  2582. }
  2583. private static void _nextMayHaveChanged(Song possibleNextSong) {
  2584. if (nextSong != possibleNextSong && nextPreparationEnabled) {
  2585. nextSong = possibleNextSong;
  2586. if (playerState == PLAYER_STATE_LOADED && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
  2587. _clearNextPlayer();
  2588. if (nextPlayer != null)
  2589. nextPlayer.reset();
  2590. if (playerState == PLAYER_STATE_LOADED)
  2591. _scheduleNextPlayerForPreparation();
  2592. }
  2593. }
  2594. private static Song stateLastSong;
  2595. private static boolean stateLastPlaying, stateLastPreparing;
  2596. private static int stateIndex;
  2597. private static final MediaPlayer[] statePlayer = new MediaPlayer[8];
  2598. private static final Throwable[] stateEx = new Throwable[8];
  2599. @SuppressWarnings("deprecation")
  2600. @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
  2601. private static void broadcastStateChangeToRemoteControl(boolean preparing, boolean titleOrSongHaveChanged) {
  2602. try {
  2603. if (localSong == null) {
  2604. remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_STOPPED);
  2605. } else {
  2606. remoteControlClient.setPlaybackState(preparing ? RemoteControlClient.PLAYSTATE_BUFFERING : (playing ? RemoteControlClient.PLAYSTATE_PLAYING : RemoteControlClient.PLAYSTATE_PAUSED));
  2607. if (titleOrSongHaveChanged) {
  2608. final RemoteControlClient.MetadataEditor ed = remoteControlClient.editMetadata(true);
  2609. ed.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, preparing ? thePlayer.getText(R.string.loading).toString() : localSong.title);
  2610. ed.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, localSong.artist);
  2611. ed.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, localSong.album);
  2612. ed.putLong(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER, localSong.track);
  2613. ed.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, localSong.lengthMS);
  2614. //Oh!!!! METADATA_KEY_YEAR is only handled in API 19+ !!! :(
  2615. //http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/media/MediaMetadataEditor.java#MediaMetadataEditor.0METADATA_KEYS_TYPE
  2616. //http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.1.2_r1/android/media/RemoteControlClient.java#RemoteControlClient.0METADATA_KEYS_TYPE_LONG
  2617. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
  2618. ed.putLong(MediaMetadataRetriever.METADATA_KEY_YEAR, localSong.year);
  2619. ed.apply();
  2620. }
  2621. }
  2622. } catch (Throwable ex) {
  2623. ex.printStackTrace();
  2624. }
  2625. }
  2626. @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  2627. private static void broadcastStateChangeToMediaSession(boolean preparing, boolean titleOrSongHaveChanged) {
  2628. try {
  2629. if (localSong == null) {
  2630. mediaSession.setPlaybackState(mediaSessionPlaybackStateBuilder.setState(PlaybackState.STATE_STOPPED, 0, 1, SystemClock.elapsedRealtime()).build());
  2631. } else {
  2632. mediaSession.setPlaybackState(mediaSessionPlaybackStateBuilder.setState(preparing ? PlaybackState.STATE_BUFFERING : (playing ? PlaybackState.STATE_PLAYING : PlaybackState.STATE_PAUSED), getPosition(), 1, SystemClock.elapsedRealtime()).build());
  2633. if (titleOrSongHaveChanged) {
  2634. mediaSessionMetadataBuilder.putString(MediaMetadata.METADATA_KEY_TITLE, preparing ? thePlayer.getText(R.string.loading).toString() : localSong.title);
  2635. mediaSessionMetadataBuilder.putString(MediaMetadata.METADATA_KEY_ARTIST, localSong.artist);
  2636. mediaSessionMetadataBuilder.putString(MediaMetadata.METADATA_KEY_ALBUM, localSong.album);
  2637. mediaSessionMetadataBuilder.putLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER, localSong.track);
  2638. mediaSessionMetadataBuilder.putLong(MediaMetadata.METADATA_KEY_DURATION, localSong.lengthMS);
  2639. mediaSessionMetadataBuilder.putLong(MediaMetadata.METADATA_KEY_YEAR, localSong.year);
  2640. mediaSession.setMetadata(mediaSessionMetadataBuilder.build());
  2641. }
  2642. }
  2643. } catch (Throwable ex) {
  2644. ex.printStackTrace();
  2645. }
  2646. }
  2647. @SuppressWarnings("deprecation")
  2648. private static void broadcastStateChange(boolean playbackHasChanged, boolean preparing, boolean titleOrSongHaveChanged) {
  2649. //
  2650. //perhaps, one day we should implement RemoteControlClient for better Bluetooth support...?
  2651. //http://developer.android.com/reference/android/media/RemoteControlClient.html
  2652. //https://android.googlesource.com/platform/packages/apps/Music/+/master/src/com/android/music/MediaPlaybackService.java
  2653. //
  2654. //http://stackoverflow.com/questions/15527614/send-track-informations-via-a2dp-avrcp
  2655. //http://stackoverflow.com/questions/14536597/how-does-the-android-lockscreen-get-playing-song
  2656. //http://stackoverflow.com/questions/10510292/how-to-get-current-music-track-info
  2657. //
  2658. //https://android.googlesource.com/platform/packages/apps/Bluetooth/+/android-4.3_r0.9.1/src/com/android/bluetooth/a2dp/Avrcp.java
  2659. //
  2660. if (localSong == null) {
  2661. stickyBroadcast.setAction("com.android.music.playbackcomplete");
  2662. stickyBroadcast.removeExtra("id");
  2663. stickyBroadcast.removeExtra("songid");
  2664. stickyBroadcast.removeExtra("track");
  2665. stickyBroadcast.removeExtra("artist");
  2666. stickyBroadcast.removeExtra("album");
  2667. stickyBroadcast.removeExtra("duration");
  2668. //stickyBroadcast.removeExtra("position");
  2669. stickyBroadcast.removeExtra("playing");
  2670. } else {
  2671. //apparently, a few 4.3 devices have an issue with com.android.music.metachanged....
  2672. stickyBroadcast.setAction(playbackHasChanged ? "com.android.music.playstatechanged" : "com.android.music.metachanged");
  2673. //stickyBroadcast.setAction("com.android.music.playstatechanged");
  2674. stickyBroadcast.putExtra("id", localSong.id);
  2675. stickyBroadcast.putExtra("songid", localSong.id);
  2676. stickyBroadcast.putExtra("track", preparing ? thePlayer.getText(R.string.loading) : localSong.title);
  2677. stickyBroadcast.putExtra("artist", localSong.artist);
  2678. stickyBroadcast.putExtra("album", localSong.album);
  2679. stickyBroadcast.putExtra("duration", (long)localSong.lengthMS);
  2680. //stickyBroadcast.putExtra("position", (long)0);
  2681. stickyBroadcast.putExtra("playing", playing);
  2682. }
  2683. //thePlayer.sendBroadcast(stickyBroadcast);
  2684. //maybe check if api >= 23, and if so, use sendBroadcast instead.....???
  2685. thePlayer.sendStickyBroadcast(stickyBroadcast);
  2686. if (remoteControlClient != null)
  2687. broadcastStateChangeToRemoteControl(preparing, titleOrSongHaveChanged);
  2688. if (mediaSession != null)
  2689. broadcastStateChangeToMediaSession(preparing, titleOrSongHaveChanged);
  2690. }
  2691. private static void updateState(int arg1, int stateIndex, Song newSong) {
  2692. localPlaying = ((arg1 & 0x04) != 0);
  2693. localPlayerState = (arg1 & 0x03);
  2694. localPlayer = statePlayer[stateIndex];
  2695. statePlayer[stateIndex] = null;
  2696. final Throwable ex = stateEx[stateIndex];
  2697. stateEx[stateIndex] = null;
  2698. localSong = newSong;
  2699. if (songs.okToTurnOffAfterReachingTheEnd) {
  2700. songs.okToTurnOffAfterReachingTheEnd = false;
  2701. //turn off if requested
  2702. if (turnOffWhenPlaylistEnds)
  2703. localHandler.sendEmptyMessageAtTime(MSG_TURN_OFF_NOW, SystemClock.uptimeMillis() + 100);
  2704. }
  2705. notificationManager.notify(1, getNotification());
  2706. final boolean songHasChanged = ((arg1 & 0x08) != 0);
  2707. final boolean playbackHasChanged = ((arg1 & 0x10) != 0);
  2708. final boolean preparing = ((arg1 & 0x20) != 0);
  2709. final boolean preparingHasChanged = ((arg1 & 0x40) != 0);
  2710. broadcastStateChange(playbackHasChanged, preparing, songHasChanged | preparingHasChanged);
  2711. if (idleTurnOffTimerSelectedMinutes > 0)
  2712. processIdleTurnOffTimer();
  2713. if (bluetoothVisualizerController != null)
  2714. updateBluetoothVisualizer(songHasChanged);
  2715. WidgetMain.updateWidgets(thePlayer);
  2716. if (ex != null) {
  2717. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && (ex instanceof PermissionDeniedException) && observer != null && (observer instanceof ClientActivity))
  2718. ((ClientActivity)observer).getHostActivity().requestStoragePermission();
  2719. final String msg = ex.getMessage();
  2720. if (ex instanceof IllegalStateException) {
  2721. UI.toast(thePlayer, R.string.error_state);
  2722. } else if (ex instanceof FileNotFoundException) {
  2723. UI.toast(thePlayer, R.string.error_file_not_found);
  2724. } else if (ex instanceof TimeoutException) {
  2725. UI.toast(thePlayer, R.string.error_timeout);
  2726. } else if (ex instanceof MediaServerDiedException) {
  2727. UI.toast(thePlayer, R.string.error_server_died);
  2728. } else if (ex instanceof SecurityException) {
  2729. UI.toast(thePlayer, R.string.error_security);
  2730. } else if (ex instanceof IOException) {
  2731. UI.toast(thePlayer, (localSong != null && localSong.isHttp && !isConnectedToTheInternet()) ? R.string.error_connection : R.string.error_io);
  2732. } else if (msg == null || msg.length() == 0) {
  2733. UI.toast(thePlayer, R.string.error_playback);
  2734. } else {
  2735. final StringBuilder sb = new StringBuilder(thePlayer.getText(R.string.error_msg));
  2736. sb.append(' ');
  2737. sb.append(msg);
  2738. UI.toast(thePlayer, sb);
  2739. }
  2740. }
  2741. if (observer != null)
  2742. observer.onPlayerChanged(localSong, songHasChanged, preparingHasChanged, ex);
  2743. }
  2744. private static void _updateState(boolean metaHasChanged, Throwable ex) {
  2745. if (localHandler != null) {
  2746. final boolean songHasChanged = (metaHasChanged || (stateLastSong != song));
  2747. final boolean playbackHasChanged = (stateLastPlaying != playing);
  2748. final boolean preparing = (playerState == PLAYER_STATE_PREPARING || playerBuffering);
  2749. final boolean preparingHasChanged = (stateLastPreparing != preparing);
  2750. if (!songHasChanged && !playbackHasChanged && !preparingHasChanged && ex == null)
  2751. return;
  2752. stateLastSong = song;
  2753. stateLastPlaying = playing;
  2754. stateLastPreparing = preparing;
  2755. final Message msg = Message.obtain(localHandler, MSG_UPDATE_STATE, playerState | (playing ? 0x04 : 0) | (songHasChanged ? 0x08 : 0) | (playbackHasChanged ? 0x10 : 0) | (preparing ? 0x20 : 0) | (preparingHasChanged ? 0x40 : 0), stateIndex, song);
  2756. statePlayer[stateIndex] = player;
  2757. stateEx[stateIndex] = ex;
  2758. stateIndex = (stateIndex + 1) & 7;
  2759. localHandler.sendMessageAtTime(msg, SystemClock.uptimeMillis());
  2760. }
  2761. }
  2762. }