PageRenderTime 75ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/app/src/main/java/com/naman14/timber/MusicService.java

https://bitbucket.org/alistair_holmes/timber
Java | 2861 lines | 2412 code | 417 blank | 32 comment | 566 complexity | 22dce5c2413ba7da576fb33553a9e1aa MD5 | raw file

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

  1. /*
  2. * Copyright (C) 2012 Andrew Neal
  3. * Copyright (C) 2014 The CyanogenMod Project
  4. * Copyright (C) 2015 Naman Dwivedi
  5. *
  6. * Licensed under the Apache License, Version 2.0
  7. * (the "License"); you may not use this file except in compliance with the
  8. * License. You may obtain a copy of the License at
  9. * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
  10. * or agreed to in writing, software distributed under the License is
  11. * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  12. * KIND, either express or implied. See the License for the specific language
  13. * governing permissions and limitations under the License.
  14. */
  15. package com.naman14.timber;
  16. import android.Manifest;
  17. import android.annotation.SuppressLint;
  18. import android.annotation.TargetApi;
  19. import android.app.AlarmManager;
  20. import android.app.Notification;
  21. import android.app.NotificationChannel;
  22. import android.app.NotificationManager;
  23. import android.app.PendingIntent;
  24. import android.app.Service;
  25. import android.content.BroadcastReceiver;
  26. import android.content.ComponentName;
  27. import android.content.ContentResolver;
  28. import android.content.Context;
  29. import android.content.Intent;
  30. import android.content.IntentFilter;
  31. import android.content.SharedPreferences;
  32. import android.database.ContentObserver;
  33. import android.database.Cursor;
  34. import android.database.MatrixCursor;
  35. import android.graphics.Bitmap;
  36. import android.graphics.Color;
  37. import android.media.AudioManager;
  38. import android.media.AudioManager.OnAudioFocusChangeListener;
  39. import android.media.MediaMetadataEditor;
  40. import android.media.MediaMetadataRetriever;
  41. import android.media.MediaPlayer;
  42. import android.media.RemoteControlClient;
  43. import android.media.audiofx.AudioEffect;
  44. import android.net.Uri;
  45. import android.os.Build;
  46. import android.os.Bundle;
  47. import android.os.Handler;
  48. import android.os.HandlerThread;
  49. import android.os.IBinder;
  50. import android.os.Looper;
  51. import android.os.Message;
  52. import android.os.PowerManager;
  53. import android.os.PowerManager.WakeLock;
  54. import android.os.RemoteException;
  55. import android.os.SystemClock;
  56. import android.provider.MediaStore;
  57. import android.provider.MediaStore.Audio.AlbumColumns;
  58. import android.provider.MediaStore.Audio.AudioColumns;
  59. import android.support.v4.app.NotificationManagerCompat;
  60. import android.support.v4.media.MediaMetadataCompat;
  61. import android.support.v4.media.session.MediaSessionCompat;
  62. import android.support.v4.media.session.PlaybackStateCompat;
  63. import android.support.v7.graphics.Palette;
  64. import android.text.TextUtils;
  65. import android.util.Log;
  66. import com.naman14.timber.helpers.MediaButtonIntentReceiver;
  67. import com.naman14.timber.helpers.MusicPlaybackTrack;
  68. import com.naman14.timber.lastfmapi.LastFmClient;
  69. import com.naman14.timber.lastfmapi.models.LastfmUserSession;
  70. import com.naman14.timber.lastfmapi.models.ScrobbleQuery;
  71. import com.naman14.timber.permissions.Nammu;
  72. import com.naman14.timber.provider.MusicPlaybackState;
  73. import com.naman14.timber.provider.RecentStore;
  74. import com.naman14.timber.provider.SongPlayCount;
  75. import com.naman14.timber.utils.NavigationUtils;
  76. import com.naman14.timber.utils.PreferencesUtility;
  77. import com.naman14.timber.utils.TimberUtils;
  78. import com.naman14.timber.utils.TimberUtils.IdType;
  79. import com.nostra13.universalimageloader.core.ImageLoader;
  80. import java.io.IOException;
  81. import java.lang.ref.WeakReference;
  82. import java.util.ArrayList;
  83. import java.util.LinkedList;
  84. import java.util.ListIterator;
  85. import java.util.Random;
  86. import java.util.TreeSet;
  87. import de.Maxr1998.trackselectorlib.ModNotInstalledException;
  88. import de.Maxr1998.trackselectorlib.NotificationHelper;
  89. import de.Maxr1998.trackselectorlib.TrackItem;
  90. @SuppressLint("NewApi")
  91. public class MusicService extends Service {
  92. public static final String PLAYSTATE_CHANGED = "com.naman14.timber.playstatechanged";
  93. public static final String POSITION_CHANGED = "com.naman14.timber.positionchanged";
  94. public static final String META_CHANGED = "com.naman14.timber.metachanged";
  95. public static final String QUEUE_CHANGED = "com.naman14.timber.queuechanged";
  96. public static final String PLAYLIST_CHANGED = "com.naman14.timber.playlistchanged";
  97. public static final String REPEATMODE_CHANGED = "com.naman14.timber.repeatmodechanged";
  98. public static final String SHUFFLEMODE_CHANGED = "com.naman14.timber.shufflemodechanged";
  99. public static final String TRACK_ERROR = "com.naman14.timber.trackerror";
  100. public static final String TIMBER_PACKAGE_NAME = "com.naman14.timber";
  101. public static final String MUSIC_PACKAGE_NAME = "com.android.music";
  102. public static final String SERVICECMD = "com.naman14.timber.musicservicecommand";
  103. public static final String TOGGLEPAUSE_ACTION = "com.naman14.timber.togglepause";
  104. public static final String PAUSE_ACTION = "com.naman14.timber.pause";
  105. public static final String STOP_ACTION = "com.naman14.timber.stop";
  106. public static final String PREVIOUS_ACTION = "com.naman14.timber.previous";
  107. public static final String PREVIOUS_FORCE_ACTION = "com.naman14.timber.previous.force";
  108. public static final String NEXT_ACTION = "fcom.naman14.timber.next";
  109. public static final String REPEAT_ACTION = "com.naman14.timber.repeat";
  110. public static final String SHUFFLE_ACTION = "com.naman14.timber.shuffle";
  111. public static final String FROM_MEDIA_BUTTON = "frommediabutton";
  112. public static final String REFRESH = "com.naman14.timber.refresh";
  113. public static final String UPDATE_LOCKSCREEN = "com.naman14.timber.updatelockscreen";
  114. public static final String CMDNAME = "command";
  115. public static final String CMDTOGGLEPAUSE = "togglepause";
  116. public static final String CMDSTOP = "stop";
  117. public static final String CMDPAUSE = "pause";
  118. public static final String CMDPLAY = "play";
  119. public static final String CMDPREVIOUS = "previous";
  120. public static final String CMDNEXT = "next";
  121. public static final String CMDNOTIF = "buttonId";
  122. public static final String UPDATE_PREFERENCES = "updatepreferences";
  123. public static final String CHANNEL_ID = "timber_channel_01";
  124. public static final int NEXT = 2;
  125. public static final int LAST = 3;
  126. public static final int SHUFFLE_NONE = 0;
  127. public static final int SHUFFLE_NORMAL = 1;
  128. public static final int SHUFFLE_AUTO = 2;
  129. public static final int REPEAT_NONE = 0;
  130. public static final int REPEAT_CURRENT = 1;
  131. public static final int REPEAT_ALL = 2;
  132. public static final int MAX_HISTORY_SIZE = 1000;
  133. private static final String TAG = "MusicPlaybackService";
  134. private static final boolean D = false;
  135. private static final String SHUTDOWN = "com.naman14.timber.shutdown";
  136. private static final int IDCOLIDX = 0;
  137. private static final int TRACK_ENDED = 1;
  138. private static final int TRACK_WENT_TO_NEXT = 2;
  139. private static final int RELEASE_WAKELOCK = 3;
  140. private static final int SERVER_DIED = 4;
  141. private static final int FOCUSCHANGE = 5;
  142. private static final int FADEDOWN = 6;
  143. private static final int FADEUP = 7;
  144. private static final int IDLE_DELAY = 5 * 60 * 1000;
  145. private static final long REWIND_INSTEAD_PREVIOUS_THRESHOLD = 3000;
  146. private static final String[] PROJECTION = new String[]{
  147. "audio._id AS _id", MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.ALBUM,
  148. MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.DATA,
  149. MediaStore.Audio.Media.MIME_TYPE, MediaStore.Audio.Media.ALBUM_ID,
  150. MediaStore.Audio.Media.ARTIST_ID
  151. };
  152. private static final String[] ALBUM_PROJECTION = new String[]{
  153. MediaStore.Audio.Albums.ALBUM, MediaStore.Audio.Albums.ARTIST,
  154. MediaStore.Audio.Albums.LAST_YEAR
  155. };
  156. private static final String[] NOTIFICATION_PROJECTION = new String[]{
  157. "audio._id AS _id", AudioColumns.ALBUM_ID, AudioColumns.TITLE,
  158. AudioColumns.ARTIST, AudioColumns.DURATION
  159. };
  160. private static final Shuffler mShuffler = new Shuffler();
  161. private static final int NOTIFY_MODE_NONE = 0;
  162. private static final int NOTIFY_MODE_FOREGROUND = 1;
  163. private static final int NOTIFY_MODE_BACKGROUND = 2;
  164. private static final String[] PROJECTION_MATRIX = new String[]{
  165. "_id", MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.ALBUM,
  166. MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.DATA,
  167. MediaStore.Audio.Media.MIME_TYPE, MediaStore.Audio.Media.ALBUM_ID,
  168. MediaStore.Audio.Media.ARTIST_ID
  169. };
  170. private static LinkedList<Integer> mHistory = new LinkedList<>();
  171. private final IBinder mBinder = new ServiceStub(this);
  172. private MultiPlayer mPlayer;
  173. private String mFileToPlay;
  174. private WakeLock mWakeLock;
  175. private AlarmManager mAlarmManager;
  176. private PendingIntent mShutdownIntent;
  177. private boolean mShutdownScheduled;
  178. private NotificationManagerCompat mNotificationManager;
  179. private Cursor mCursor;
  180. private Cursor mAlbumCursor;
  181. private AudioManager mAudioManager;
  182. private SharedPreferences mPreferences;
  183. private boolean mServiceInUse = false;
  184. private boolean mIsSupposedToBePlaying = false;
  185. private long mLastPlayedTime;
  186. private int mNotifyMode = NOTIFY_MODE_NONE;
  187. private long mNotificationPostTime = 0;
  188. private boolean mQueueIsSaveable = true;
  189. private boolean mPausedByTransientLossOfFocus = false;
  190. private MediaSessionCompat mSession;
  191. @SuppressWarnings("deprecation")
  192. private RemoteControlClient mRemoteControlClient;
  193. private ComponentName mMediaButtonReceiverComponent;
  194. private int mCardId;
  195. private int mPlayPos = -1;
  196. private int mNextPlayPos = -1;
  197. private int mOpenFailedCounter = 0;
  198. private int mMediaMountedCount = 0;
  199. private int mShuffleMode = SHUFFLE_NONE;
  200. private int mRepeatMode = REPEAT_NONE;
  201. private int mServiceStartId = -1;
  202. private ArrayList<MusicPlaybackTrack> mPlaylist = new ArrayList<MusicPlaybackTrack>(100);
  203. private long[] mAutoShuffleList = null;
  204. private MusicPlayerHandler mPlayerHandler;
  205. private final OnAudioFocusChangeListener mAudioFocusListener = new OnAudioFocusChangeListener() {
  206. @Override
  207. public void onAudioFocusChange(final int focusChange) {
  208. mPlayerHandler.obtainMessage(FOCUSCHANGE, focusChange, 0).sendToTarget();
  209. }
  210. };
  211. private HandlerThread mHandlerThread;
  212. private BroadcastReceiver mUnmountReceiver = null;
  213. private MusicPlaybackState mPlaybackStateStore;
  214. private boolean mShowAlbumArtOnLockscreen;
  215. private boolean mActivateXTrackSelector;
  216. private SongPlayCount mSongPlayCount;
  217. private RecentStore mRecentStore;
  218. private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
  219. @Override
  220. public void onReceive(final Context context, final Intent intent) {
  221. final String command = intent.getStringExtra(CMDNAME);
  222. handleCommandIntent(intent);
  223. }
  224. };
  225. private ContentObserver mMediaStoreObserver;
  226. @Override
  227. public IBinder onBind(final Intent intent) {
  228. if (D) Log.d(TAG, "Service bound, intent = " + intent);
  229. cancelShutdown();
  230. mServiceInUse = true;
  231. return mBinder;
  232. }
  233. @Override
  234. public boolean onUnbind(final Intent intent) {
  235. if (D) Log.d(TAG, "Service unbound");
  236. mServiceInUse = false;
  237. saveQueue(true);
  238. if (mIsSupposedToBePlaying || mPausedByTransientLossOfFocus) {
  239. return true;
  240. } else if (mPlaylist.size() > 0 || mPlayerHandler.hasMessages(TRACK_ENDED)) {
  241. scheduleDelayedShutdown();
  242. return true;
  243. }
  244. stopSelf(mServiceStartId);
  245. return true;
  246. }
  247. @Override
  248. public void onRebind(final Intent intent) {
  249. cancelShutdown();
  250. mServiceInUse = true;
  251. }
  252. @Override
  253. public void onCreate() {
  254. if (D) Log.d(TAG, "Creating service");
  255. super.onCreate();
  256. mNotificationManager = NotificationManagerCompat.from(this);
  257. createNotificationChannel();
  258. // gets a pointer to the playback state store
  259. mPlaybackStateStore = MusicPlaybackState.getInstance(this);
  260. mSongPlayCount = SongPlayCount.getInstance(this);
  261. mRecentStore = RecentStore.getInstance(this);
  262. mHandlerThread = new HandlerThread("MusicPlayerHandler",
  263. android.os.Process.THREAD_PRIORITY_BACKGROUND);
  264. mHandlerThread.start();
  265. mPlayerHandler = new MusicPlayerHandler(this, mHandlerThread.getLooper());
  266. mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
  267. mMediaButtonReceiverComponent = new ComponentName(getPackageName(),
  268. MediaButtonIntentReceiver.class.getName());
  269. mAudioManager.registerMediaButtonEventReceiver(mMediaButtonReceiverComponent);
  270. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
  271. setUpMediaSession();
  272. else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
  273. setUpRemoteControlClient();
  274. mPreferences = getSharedPreferences("Service", 0);
  275. mCardId = getCardId();
  276. registerExternalStorageListener();
  277. mPlayer = new MultiPlayer(this);
  278. mPlayer.setHandler(mPlayerHandler);
  279. // Initialize the intent filter and each action
  280. final IntentFilter filter = new IntentFilter();
  281. filter.addAction(SERVICECMD);
  282. filter.addAction(TOGGLEPAUSE_ACTION);
  283. filter.addAction(PAUSE_ACTION);
  284. filter.addAction(STOP_ACTION);
  285. filter.addAction(NEXT_ACTION);
  286. filter.addAction(PREVIOUS_ACTION);
  287. filter.addAction(PREVIOUS_FORCE_ACTION);
  288. filter.addAction(REPEAT_ACTION);
  289. filter.addAction(SHUFFLE_ACTION);
  290. filter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
  291. filter.addAction(Intent.ACTION_SCREEN_ON);
  292. // Attach the broadcast listener
  293. registerReceiver(mIntentReceiver, filter);
  294. mMediaStoreObserver = new MediaStoreObserver(mPlayerHandler);
  295. getContentResolver().registerContentObserver(
  296. MediaStore.Audio.Media.INTERNAL_CONTENT_URI, true, mMediaStoreObserver);
  297. getContentResolver().registerContentObserver(
  298. MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, true, mMediaStoreObserver);
  299. // Initialize the wake lock
  300. final PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
  301. mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName());
  302. mWakeLock.setReferenceCounted(false);
  303. final Intent shutdownIntent = new Intent(this, MusicService.class);
  304. shutdownIntent.setAction(SHUTDOWN);
  305. mAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
  306. mShutdownIntent = PendingIntent.getService(this, 0, shutdownIntent, 0);
  307. scheduleDelayedShutdown();
  308. reloadQueueAfterPermissionCheck();
  309. notifyChange(QUEUE_CHANGED);
  310. notifyChange(META_CHANGED);
  311. //Try to push LastFMCache
  312. if (LastfmUserSession.getSession(this) != null) {
  313. LastFmClient.getInstance(this).Scrobble(null);
  314. }
  315. PreferencesUtility pref = PreferencesUtility.getInstance(this);
  316. mShowAlbumArtOnLockscreen = pref.getSetAlbumartLockscreen();
  317. mActivateXTrackSelector = pref.getXPosedTrackselectorEnabled();
  318. }
  319. @SuppressWarnings("deprecation")
  320. @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
  321. private void setUpRemoteControlClient() {
  322. //Legacy for ICS
  323. if (mRemoteControlClient == null) {
  324. Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
  325. mediaButtonIntent.setComponent(mMediaButtonReceiverComponent);
  326. PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(this, 0, mediaButtonIntent, 0);
  327. // create and register the remote control client
  328. mRemoteControlClient = new RemoteControlClient(mediaPendingIntent);
  329. mAudioManager.registerRemoteControlClient(mRemoteControlClient);
  330. }
  331. mRemoteControlClient.setTransportControlFlags(
  332. RemoteControlClient.FLAG_KEY_MEDIA_PLAY |
  333. RemoteControlClient.FLAG_KEY_MEDIA_PAUSE |
  334. RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS |
  335. RemoteControlClient.FLAG_KEY_MEDIA_NEXT |
  336. RemoteControlClient.FLAG_KEY_MEDIA_STOP);
  337. }
  338. private void setUpMediaSession() {
  339. mSession = new MediaSessionCompat(this, "Timber");
  340. mSession.setCallback(new MediaSessionCompat.Callback() {
  341. @Override
  342. public void onPause() {
  343. pause();
  344. mPausedByTransientLossOfFocus = false;
  345. }
  346. @Override
  347. public void onPlay() {
  348. play();
  349. }
  350. @Override
  351. public void onSeekTo(long pos) {
  352. seek(pos);
  353. }
  354. @Override
  355. public void onSkipToNext() {
  356. gotoNext(true);
  357. }
  358. @Override
  359. public void onSkipToPrevious() {
  360. prev(false);
  361. }
  362. @Override
  363. public void onStop() {
  364. pause();
  365. mPausedByTransientLossOfFocus = false;
  366. seek(0);
  367. releaseServiceUiAndStop();
  368. }
  369. });
  370. mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
  371. | MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS);
  372. }
  373. @Override
  374. public void onDestroy() {
  375. if (D) Log.d(TAG, "Destroying service");
  376. super.onDestroy();
  377. //Try to push LastFMCache
  378. if (LastfmUserSession.getSession(this).isLogedin()) {
  379. LastFmClient.getInstance(this).Scrobble(null);
  380. }
  381. // Remove any sound effects
  382. final Intent audioEffectsIntent = new Intent(
  383. AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
  384. audioEffectsIntent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, getAudioSessionId());
  385. audioEffectsIntent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName());
  386. sendBroadcast(audioEffectsIntent);
  387. mAlarmManager.cancel(mShutdownIntent);
  388. mPlayerHandler.removeCallbacksAndMessages(null);
  389. if (TimberUtils.isJellyBeanMR2())
  390. mHandlerThread.quitSafely();
  391. else mHandlerThread.quit();
  392. mPlayer.release();
  393. mPlayer = null;
  394. mAudioManager.abandonAudioFocus(mAudioFocusListener);
  395. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
  396. mSession.release();
  397. getContentResolver().unregisterContentObserver(mMediaStoreObserver);
  398. closeCursor();
  399. unregisterReceiver(mIntentReceiver);
  400. if (mUnmountReceiver != null) {
  401. unregisterReceiver(mUnmountReceiver);
  402. mUnmountReceiver = null;
  403. }
  404. mWakeLock.release();
  405. }
  406. @Override
  407. public int onStartCommand(final Intent intent, final int flags, final int startId) {
  408. if (D) Log.d(TAG, "Got new intent " + intent + ", startId = " + startId);
  409. mServiceStartId = startId;
  410. if (intent != null) {
  411. final String action = intent.getAction();
  412. if (SHUTDOWN.equals(action)) {
  413. mShutdownScheduled = false;
  414. releaseServiceUiAndStop();
  415. return START_NOT_STICKY;
  416. }
  417. handleCommandIntent(intent);
  418. }
  419. scheduleDelayedShutdown();
  420. if (intent != null && intent.getBooleanExtra(FROM_MEDIA_BUTTON, false)) {
  421. MediaButtonIntentReceiver.completeWakefulIntent(intent);
  422. }
  423. return START_NOT_STICKY; //no sense to use START_STICKY with using startForeground
  424. }
  425. void scrobble() {
  426. if (LastfmUserSession.getSession(this).isLogedin()) {
  427. Log.d("Scrobble", "to LastFM");
  428. String trackname = getTrackName();
  429. if (trackname != null)
  430. LastFmClient.getInstance(this).Scrobble(new ScrobbleQuery(getArtistName(), trackname, System.currentTimeMillis() / 1000));
  431. }
  432. }
  433. private void releaseServiceUiAndStop() {
  434. if (isPlaying()
  435. || mPausedByTransientLossOfFocus
  436. || mPlayerHandler.hasMessages(TRACK_ENDED)) {
  437. return;
  438. }
  439. if (D) Log.d(TAG, "Nothing is playing anymore, releasing notification");
  440. cancelNotification();
  441. mAudioManager.abandonAudioFocus(mAudioFocusListener);
  442. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
  443. mSession.setActive(false);
  444. if (!mServiceInUse) {
  445. saveQueue(true);
  446. stopSelf(mServiceStartId);
  447. }
  448. }
  449. private void handleCommandIntent(Intent intent) {
  450. final String action = intent.getAction();
  451. final String command = SERVICECMD.equals(action) ? intent.getStringExtra(CMDNAME) : null;
  452. if (D) Log.d(TAG, "handleCommandIntent: action = " + action + ", command = " + command);
  453. if (NotificationHelper.checkIntent(intent)) {
  454. goToPosition(mPlayPos + NotificationHelper.getPosition(intent));
  455. return;
  456. }
  457. if (CMDNEXT.equals(command) || NEXT_ACTION.equals(action)) {
  458. gotoNext(true);
  459. } else if (CMDPREVIOUS.equals(command) || PREVIOUS_ACTION.equals(action)
  460. || PREVIOUS_FORCE_ACTION.equals(action)) {
  461. prev(PREVIOUS_FORCE_ACTION.equals(action));
  462. } else if (CMDTOGGLEPAUSE.equals(command) || TOGGLEPAUSE_ACTION.equals(action)) {
  463. if (isPlaying()) {
  464. pause();
  465. mPausedByTransientLossOfFocus = false;
  466. } else {
  467. play();
  468. }
  469. } else if (CMDPAUSE.equals(command) || PAUSE_ACTION.equals(action)) {
  470. pause();
  471. mPausedByTransientLossOfFocus = false;
  472. } else if (CMDPLAY.equals(command)) {
  473. play();
  474. } else if (CMDSTOP.equals(command) || STOP_ACTION.equals(action)) {
  475. pause();
  476. mPausedByTransientLossOfFocus = false;
  477. seek(0);
  478. releaseServiceUiAndStop();
  479. } else if (REPEAT_ACTION.equals(action)) {
  480. cycleRepeat();
  481. } else if (SHUFFLE_ACTION.equals(action)) {
  482. cycleShuffle();
  483. } else if (UPDATE_PREFERENCES.equals(action)) {
  484. onPreferencesUpdate(intent.getExtras());
  485. }
  486. else if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(action)) {
  487. if (PreferencesUtility.getInstance(getApplicationContext()).pauseEnabledOnDetach()) {
  488. pause();
  489. }
  490. }
  491. }
  492. private void onPreferencesUpdate(Bundle extras) {
  493. mShowAlbumArtOnLockscreen = extras.getBoolean("lockscreen", mShowAlbumArtOnLockscreen);
  494. mActivateXTrackSelector = extras.getBoolean("xtrack",mActivateXTrackSelector);
  495. LastfmUserSession session = LastfmUserSession.getSession(this);
  496. session.mToken = extras.getString("lf_token", session.mToken);
  497. session.mUsername = extras.getString("lf_user", session.mUsername);
  498. if ("logout".equals(session.mToken)) {
  499. session.mToken = null;
  500. session.mUsername = null;
  501. }
  502. notifyChange(META_CHANGED);
  503. }
  504. private void updateNotification() {
  505. final int newNotifyMode;
  506. if (isPlaying()) {
  507. newNotifyMode = NOTIFY_MODE_FOREGROUND;
  508. } else if (recentlyPlayed()) {
  509. newNotifyMode = NOTIFY_MODE_BACKGROUND;
  510. } else {
  511. newNotifyMode = NOTIFY_MODE_NONE;
  512. }
  513. int notificationId = hashCode();
  514. if (mNotifyMode != newNotifyMode) {
  515. if (mNotifyMode == NOTIFY_MODE_FOREGROUND) {
  516. if (TimberUtils.isLollipop())
  517. stopForeground(newNotifyMode == NOTIFY_MODE_NONE);
  518. else
  519. stopForeground(newNotifyMode == NOTIFY_MODE_NONE || newNotifyMode == NOTIFY_MODE_BACKGROUND);
  520. } else if (newNotifyMode == NOTIFY_MODE_NONE) {
  521. mNotificationManager.cancel(notificationId);
  522. mNotificationPostTime = 0;
  523. }
  524. }
  525. if (newNotifyMode == NOTIFY_MODE_FOREGROUND) {
  526. startForeground(notificationId, buildNotification());
  527. } else if (newNotifyMode == NOTIFY_MODE_BACKGROUND) {
  528. mNotificationManager.notify(notificationId, buildNotification());
  529. }
  530. mNotifyMode = newNotifyMode;
  531. }
  532. private void cancelNotification() {
  533. stopForeground(true);
  534. mNotificationManager.cancel(hashCode());
  535. mNotificationPostTime = 0;
  536. mNotifyMode = NOTIFY_MODE_NONE;
  537. }
  538. private int getCardId() {
  539. if (TimberUtils.isMarshmallow()) {
  540. if (Nammu.checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE)) {
  541. return getmCardId();
  542. } else return 0;
  543. } else {
  544. return getmCardId();
  545. }
  546. }
  547. private int getmCardId() {
  548. final ContentResolver resolver = getContentResolver();
  549. Cursor cursor = resolver.query(Uri.parse("content://media/external/fs_id"), null, null,
  550. null, null);
  551. int mCardId = -1;
  552. if (cursor != null && cursor.moveToFirst()) {
  553. mCardId = cursor.getInt(0);
  554. cursor.close();
  555. cursor = null;
  556. }
  557. return mCardId;
  558. }
  559. public void closeExternalStorageFiles(final String storagePath) {
  560. stop(true);
  561. notifyChange(QUEUE_CHANGED);
  562. notifyChange(META_CHANGED);
  563. }
  564. public void registerExternalStorageListener() {
  565. if (mUnmountReceiver == null) {
  566. mUnmountReceiver = new BroadcastReceiver() {
  567. @Override
  568. public void onReceive(final Context context, final Intent intent) {
  569. final String action = intent.getAction();
  570. if (action.equals(Intent.ACTION_MEDIA_EJECT)) {
  571. saveQueue(true);
  572. mQueueIsSaveable = false;
  573. closeExternalStorageFiles(intent.getData().getPath());
  574. } else if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
  575. mMediaMountedCount++;
  576. mCardId = getCardId();
  577. reloadQueueAfterPermissionCheck();
  578. mQueueIsSaveable = true;
  579. notifyChange(QUEUE_CHANGED);
  580. notifyChange(META_CHANGED);
  581. }
  582. }
  583. };
  584. final IntentFilter filter = new IntentFilter();
  585. filter.addAction(Intent.ACTION_MEDIA_EJECT);
  586. filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
  587. filter.addDataScheme("file");
  588. registerReceiver(mUnmountReceiver, filter);
  589. }
  590. }
  591. private void scheduleDelayedShutdown() {
  592. if (D) Log.v(TAG, "Scheduling shutdown in " + IDLE_DELAY + " ms");
  593. mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
  594. SystemClock.elapsedRealtime() + IDLE_DELAY, mShutdownIntent);
  595. mShutdownScheduled = true;
  596. }
  597. private void cancelShutdown() {
  598. if (D) Log.d(TAG, "Cancelling delayed shutdown, scheduled = " + mShutdownScheduled);
  599. if (mShutdownScheduled) {
  600. mAlarmManager.cancel(mShutdownIntent);
  601. mShutdownScheduled = false;
  602. }
  603. }
  604. private void stop(final boolean goToIdle) {
  605. if (D) Log.d(TAG, "Stopping playback, goToIdle = " + goToIdle);
  606. long duration = this.duration();
  607. long position = this.position();
  608. if (duration > 30000 && (position >= duration / 2) || position > 240000) {
  609. scrobble();
  610. }
  611. if (mPlayer.isInitialized()) {
  612. mPlayer.stop();
  613. }
  614. mFileToPlay = null;
  615. closeCursor();
  616. if (goToIdle) {
  617. setIsSupposedToBePlaying(false, false);
  618. } else {
  619. if (TimberUtils.isLollipop())
  620. stopForeground(false);
  621. else stopForeground(true);
  622. }
  623. }
  624. private int removeTracksInternal(int first, int last) {
  625. synchronized (this) {
  626. if (last < first) {
  627. return 0;
  628. } else if (first < 0) {
  629. first = 0;
  630. } else if (last >= mPlaylist.size()) {
  631. last = mPlaylist.size() - 1;
  632. }
  633. boolean gotonext = false;
  634. if (first <= mPlayPos && mPlayPos <= last) {
  635. mPlayPos = first;
  636. gotonext = true;
  637. } else if (mPlayPos > last) {
  638. mPlayPos -= last - first + 1;
  639. }
  640. final int numToRemove = last - first + 1;
  641. if (first == 0 && last == mPlaylist.size() - 1) {
  642. mPlayPos = -1;
  643. mNextPlayPos = -1;
  644. mPlaylist.clear();
  645. mHistory.clear();
  646. } else {
  647. for (int i = 0; i < numToRemove; i++) {
  648. mPlaylist.remove(first);
  649. }
  650. ListIterator<Integer> positionIterator = mHistory.listIterator();
  651. while (positionIterator.hasNext()) {
  652. int pos = positionIterator.next();
  653. if (pos >= first && pos <= last) {
  654. positionIterator.remove();
  655. } else if (pos > last) {
  656. positionIterator.set(pos - numToRemove);
  657. }
  658. }
  659. }
  660. if (gotonext) {
  661. if (mPlaylist.size() == 0) {
  662. stop(true);
  663. mPlayPos = -1;
  664. closeCursor();
  665. } else {
  666. if (mShuffleMode != SHUFFLE_NONE) {
  667. mPlayPos = getNextPosition(true);
  668. } else if (mPlayPos >= mPlaylist.size()) {
  669. mPlayPos = 0;
  670. }
  671. final boolean wasPlaying = isPlaying();
  672. stop(false);
  673. openCurrentAndNext();
  674. if (wasPlaying) {
  675. play();
  676. }
  677. }
  678. notifyChange(META_CHANGED);
  679. }
  680. return last - first + 1;
  681. }
  682. }
  683. private void addToPlayList(final long[] list, int position, long sourceId, TimberUtils.IdType sourceType) {
  684. final int addlen = list.length;
  685. if (position < 0) {
  686. mPlaylist.clear();
  687. position = 0;
  688. }
  689. mPlaylist.ensureCapacity(mPlaylist.size() + addlen);
  690. if (position > mPlaylist.size()) {
  691. position = mPlaylist.size();
  692. }
  693. final ArrayList<MusicPlaybackTrack> arrayList = new ArrayList<MusicPlaybackTrack>(addlen);
  694. for (int i = 0; i < list.length; i++) {
  695. arrayList.add(new MusicPlaybackTrack(list[i], sourceId, sourceType, i));
  696. }
  697. mPlaylist.addAll(position, arrayList);
  698. if (mPlaylist.size() == 0) {
  699. closeCursor();
  700. notifyChange(META_CHANGED);
  701. }
  702. }
  703. private void updateCursor(final long trackId) {
  704. updateCursor("_id=" + trackId, null);
  705. }
  706. private void updateCursor(final String selection, final String[] selectionArgs) {
  707. synchronized (this) {
  708. closeCursor();
  709. mCursor = openCursorAndGoToFirst(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
  710. PROJECTION, selection, selectionArgs);
  711. }
  712. updateAlbumCursor();
  713. }
  714. private void updateCursor(final Uri uri) {
  715. synchronized (this) {
  716. closeCursor();
  717. mCursor = openCursorAndGoToFirst(uri, PROJECTION, null, null);
  718. }
  719. updateAlbumCursor();
  720. }
  721. private void updateAlbumCursor() {
  722. long albumId = getAlbumId();
  723. if (albumId >= 0) {
  724. mAlbumCursor = openCursorAndGoToFirst(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
  725. ALBUM_PROJECTION, "_id=" + albumId, null);
  726. } else {
  727. mAlbumCursor = null;
  728. }
  729. }
  730. private Cursor openCursorAndGoToFirst(Uri uri, String[] projection,
  731. String selection, String[] selectionArgs) {
  732. Cursor c = getContentResolver().query(uri, projection,
  733. selection, selectionArgs, null);
  734. if (c == null) {
  735. return null;
  736. }
  737. if (!c.moveToFirst()) {
  738. c.close();
  739. return null;
  740. }
  741. return c;
  742. }
  743. private synchronized void closeCursor() {
  744. if (mCursor != null) {
  745. mCursor.close();
  746. mCursor = null;
  747. }
  748. if (mAlbumCursor != null) {
  749. mAlbumCursor.close();
  750. mAlbumCursor = null;
  751. }
  752. }
  753. private void openCurrentAndNext() {
  754. openCurrentAndMaybeNext(true);
  755. }
  756. private void openCurrentAndMaybeNext(final boolean openNext) {
  757. synchronized (this) {
  758. closeCursor();
  759. if (mPlaylist.size() == 0) {
  760. return;
  761. }
  762. stop(false);
  763. boolean shutdown = false;
  764. updateCursor(mPlaylist.get(mPlayPos).mId);
  765. while (true) {
  766. if (mCursor != null
  767. && openFile(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "/"
  768. + mCursor.getLong(IDCOLIDX))) {
  769. break;
  770. }
  771. closeCursor();
  772. if (mOpenFailedCounter++ < 10 && mPlaylist.size() > 1) {
  773. final int pos = getNextPosition(false);
  774. if (pos < 0) {
  775. shutdown = true;
  776. break;
  777. }
  778. mPlayPos = pos;
  779. stop(false);
  780. mPlayPos = pos;
  781. updateCursor(mPlaylist.get(mPlayPos).mId);
  782. } else {
  783. mOpenFailedCounter = 0;
  784. Log.w(TAG, "Failed to open file for playback");
  785. shutdown = true;
  786. break;
  787. }
  788. }
  789. if (shutdown) {
  790. scheduleDelayedShutdown();
  791. if (mIsSupposedToBePlaying) {
  792. mIsSupposedToBePlaying = false;
  793. notifyChange(PLAYSTATE_CHANGED);
  794. }
  795. } else if (openNext) {
  796. setNextTrack();
  797. }
  798. }
  799. }
  800. private void sendErrorMessage(final String trackName) {
  801. final Intent i = new Intent(TRACK_ERROR);
  802. i.putExtra(TrackErrorExtra.TRACK_NAME, trackName);
  803. sendBroadcast(i);
  804. }
  805. private int getNextPosition(final boolean force) {
  806. if (mPlaylist == null || mPlaylist.isEmpty()) {
  807. return -1;
  808. }
  809. if (!force && mRepeatMode == REPEAT_CURRENT) {
  810. if (mPlayPos < 0) {
  811. return 0;
  812. }
  813. return mPlayPos;
  814. } else if (mShuffleMode == SHUFFLE_NORMAL) {
  815. final int numTracks = mPlaylist.size();
  816. final int[] trackNumPlays = new int[numTracks];
  817. for (int i = 0; i < numTracks; i++) {
  818. trackNumPlays[i] = 0;
  819. }
  820. final int numHistory = mHistory.size();
  821. for (int i = 0; i < numHistory; i++) {
  822. final int idx = mHistory.get(i).intValue();
  823. if (idx >= 0 && idx < numTracks) {
  824. trackNumPlays[idx]++;
  825. }
  826. }
  827. if (mPlayPos >= 0 && mPlayPos < numTracks) {
  828. trackNumPlays[mPlayPos]++;
  829. }
  830. int minNumPlays = Integer.MAX_VALUE;
  831. int numTracksWithMinNumPlays = 0;
  832. for (int i = 0; i < trackNumPlays.length; i++) {
  833. if (trackNumPlays[i] < minNumPlays) {
  834. minNumPlays = trackNumPlays[i];
  835. numTracksWithMinNumPlays = 1;
  836. } else if (trackNumPlays[i] == minNumPlays) {
  837. numTracksWithMinNumPlays++;
  838. }
  839. }
  840. if (minNumPlays > 0 && numTracksWithMinNumPlays == numTracks
  841. && mRepeatMode != REPEAT_ALL && !force) {
  842. return -1;
  843. }
  844. int skip = mShuffler.nextInt(numTracksWithMinNumPlays);
  845. for (int i = 0; i < trackNumPlays.length; i++) {
  846. if (trackNumPlays[i] == minNumPlays) {
  847. if (skip == 0) {
  848. return i;
  849. } else {
  850. skip--;
  851. }
  852. }
  853. }
  854. if (D)
  855. Log.e(TAG, "Getting the next position resulted did not get a result when it should have");
  856. return -1;
  857. } else if (mShuffleMode == SHUFFLE_AUTO) {
  858. doAutoShuffleUpdate();
  859. return mPlayPos + 1;
  860. } else {
  861. if (mPlayPos >= mPlaylist.size() - 1) {
  862. if (mRepeatMode == REPEAT_NONE && !force) {
  863. return -1;
  864. } else if (mRepeatMode == REPEAT_ALL || force) {
  865. return 0;
  866. }
  867. return -1;
  868. } else {
  869. return mPlayPos + 1;
  870. }
  871. }
  872. }
  873. private void setNextTrack() {
  874. setNextTrack(getNextPosition(false));
  875. }
  876. private void setNextTrack(int position) {
  877. mNextPlayPos = position;
  878. if (D) Log.d(TAG, "setNextTrack: next play position = " + mNextPlayPos);
  879. if (mNextPlayPos >= 0 && mPlaylist != null && mNextPlayPos < mPlaylist.size()) {
  880. final long id = mPlaylist.get(mNextPlayPos).mId;
  881. mPlayer.setNextDataSource(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "/" + id);
  882. } else {
  883. mPlayer.setNextDataSource(null);
  884. }
  885. }
  886. private boolean makeAutoShuffleList() {
  887. Cursor cursor = null;
  888. try {
  889. cursor = getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
  890. new String[]{
  891. MediaStore.Audio.Media._ID
  892. }, MediaStore.Audio.Media.IS_MUSIC + "=1", null, null);
  893. if (cursor == null || cursor.getCount() == 0) {
  894. return false;
  895. }
  896. final int len = cursor.getCount();
  897. final long[] list = new long[len];
  898. for (int i = 0; i < len; i++) {
  899. cursor.moveToNext();
  900. list[i] = cursor.getLong(0);
  901. }
  902. mAutoShuffleList = list;
  903. return true;
  904. } catch (final RuntimeException e) {
  905. } finally {
  906. if (cursor != null) {
  907. cursor.close();
  908. cursor = null;
  909. }
  910. }
  911. return false;
  912. }
  913. private void doAutoShuffleUpdate() {
  914. boolean notify = false;
  915. if (mPlayPos > 10) {
  916. removeTracks(0, mPlayPos - 9);
  917. notify = true;
  918. }
  919. final int toAdd = 7 - (mPlaylist.size() - (mPlayPos < 0 ? -1 : mPlayPos));
  920. for (int i = 0; i < toAdd; i++) {
  921. int lookback = mHistory.size();
  922. int idx = -1;
  923. while (true) {
  924. idx = mShuffler.nextInt(mAutoShuffleList.length);
  925. if (!wasRecentlyUsed(idx, lookback)) {
  926. break;
  927. }
  928. lookback /= 2;
  929. }
  930. mHistory.add(idx);
  931. if (mHistory.size() > MAX_HISTORY_SIZE) {
  932. mHistory.remove(0);
  933. }
  934. mPlaylist.add(new MusicPlaybackTrack(mAutoShuffleList[idx], -1, TimberUtils.IdType.NA, -1));
  935. notify = true;
  936. }
  937. if (notify) {
  938. notifyChange(QUEUE_CHANGED);
  939. }
  940. }
  941. private boolean wasRecentlyUsed(final int idx, int lookbacksize) {
  942. if (lookbacksize == 0) {
  943. return false;
  944. }
  945. final int histsize = mHistory.size();
  946. if (histsize < lookbacksize) {
  947. lookbacksize = histsize;
  948. }
  949. final int maxidx = histsize - 1;
  950. for (int i = 0; i < lookbacksize; i++) {
  951. final long entry = mHistory.get(maxidx - i);
  952. if (entry == idx) {
  953. return true;
  954. }
  955. }
  956. return false;
  957. }
  958. private void notifyChange(final String what) {
  959. if (D) Log.d(TAG, "notifyChange: what = " + what);
  960. // Update the lockscreen controls
  961. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
  962. updateMediaSession(what);
  963. else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
  964. updateRemoteControlClient(what);
  965. if (what.equals(POSITION_CHANGED)) {
  966. return;
  967. }
  968. final Intent intent = new Intent(what);
  969. intent.putExtra("id", getAudioId());
  970. intent.putExtra("artist", getArtistName());
  971. intent.putExtra("album", getAlbumName());
  972. intent.putExtra("albumid", getAlbumId());
  973. intent.putExtra("track", getTrackName());
  974. intent.putExtra("playing", isPlaying());
  975. sendStickyBroadcast(intent);
  976. final Intent musicIntent = new Intent(intent);
  977. musicIntent.setAction(what.replace(TIMBER_PACKAGE_NAME, MUSIC_PACKAGE_NAME));
  978. sendStickyBroadcast(musicIntent);
  979. if (what.equals(META_CHANGED)) {
  980. mRecentStore.addSongId(getAudioId());
  981. mSongPlayCount.bumpSongCount(getAudioId());
  982. } else if (what.equals(QUEUE_CHANGED)) {
  983. saveQueue(true);
  984. if (isPlaying()) {
  985. if (mNextPlayPos >= 0 && mNextPlayPos < mPlaylist.size()
  986. && getShuffleMode() != SHUFFLE_NONE) {
  987. setNextTrack(mNextPlayPos);
  988. } else {
  989. setNextTrack();
  990. }
  991. }
  992. } else {
  993. saveQueue(false);
  994. }
  995. if (what.equals(PLAYSTATE_CHANGED)) {
  996. updateNotification();
  997. }
  998. }
  999. @SuppressWarnings("deprecation")
  1000. @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
  1001. private void updateRemoteControlClient(final String what) {
  1002. //Legacy for ICS
  1003. if (mRemoteControlClient != null) {
  1004. int playState = mIsSupposedToBePlaying
  1005. ? RemoteControlClient.PLAYSTATE_PLAYING
  1006. : RemoteControlClient.PLAYSTATE_PAUSED;
  1007. if (what.equals(META_CHANGED) || what.equals(QUEUE_CHANGED)) {
  1008. Bitmap albumArt = null;
  1009. if (mShowAlbumArtOnLockscreen) {
  1010. albumArt = ImageLoader.getInstance().loadImageSync(TimberUtils.getAlbumArtUri(getAlbumId()).toString());
  1011. if (albumArt != null) {
  1012. Bitmap.Config config = albumArt.getConfig();
  1013. if (config == null) {
  1014. config = Bitmap.Config.ARGB_8888;
  1015. }
  1016. albumArt = albumArt.copy(config, false);
  1017. }
  1018. }
  1019. RemoteControlClient.MetadataEditor editor = mRemoteControlClient.editMetadata(true);
  1020. editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, getAlbumName());
  1021. editor.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, getArtistName());
  1022. editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, getTrackName());
  1023. editor.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, duration());
  1024. editor.putBitmap(MediaMetadataEditor.BITMAP_KEY_ARTWORK, albumArt);
  1025. editor.apply();
  1026. }
  1027. mRemoteControlClient.setPlaybackState(playState);
  1028. }
  1029. }
  1030. private void updateMediaSession(final String what) {
  1031. int playState = mIsSupposedToBePlaying
  1032. ? PlaybackStateCompat.STATE_PLAYING
  1033. : PlaybackStateCompat.STATE_PAUSED;
  1034. if (what.equals(PLAYSTATE_CHANGED) || what.equals(POSITION_CHANGED)) {
  1035. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  1036. mSession.setPlaybackState(new PlaybackStateCompat.Builder()
  1037. .setState(playState, position(), 1.0f)
  1038. .setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY_PAUSE |
  1039. PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)
  1040. .build());
  1041. }
  1042. } else if (what.equals(META_CHANGED) || what.equals(QUEUE_CHANGED)) {
  1043. Bitmap albumArt = null;
  1044. if (mShowAlbumArtOnLockscreen) {
  1045. albumArt = ImageLoader.getInstance().loadImageSync(TimberUtils.getAlbumArtUri(getAlbumId()).toString());
  1046. if (albumArt != null) {
  1047. Bitmap.Config config = albumArt.getConfig();
  1048. if (config == null) {
  1049. config = Bitmap.Config.ARGB_8888;
  1050. }
  1051. albumArt = albumArt.copy(config, false);
  1052. }
  1053. }
  1054. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  1055. mSession.setMetadata(new MediaMetadataCompat.Builder()
  1056. .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, getArtistName())
  1057. .putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, getAlbumArtistName())
  1058. .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, getAlbumName())
  1059. .putString(MediaMetadataCompat.METADATA_KEY_TITLE, getTrackName())
  1060. .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration())
  1061. .putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, getQueuePosition() + 1)
  1062. .putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, getQueue().length)
  1063. .putString(MediaMetadataCompat.METADATA_KEY_GENRE, getGenreName())
  1064. .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, albumArt)
  1065. .build());
  1066. mSession.setPlaybackState(new PlaybackStateCompat.Builder()
  1067. .setState(playState, position(), 1.0f)
  1068. .setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY_PAUSE |
  1069. PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)
  1070. .build());
  1071. }
  1072. }
  1073. }
  1074. private void createNotificationChannel() {
  1075. if (TimberUtils.isOreo()) {
  1076. CharSequence name = "Timber";
  1077. int importance = NotificationManager.IMPORTANCE_LOW;
  1078. NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
  1079. NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, importance);
  1080. manager.createNotificationChannel(mChannel);
  1081. }
  1082. }
  1083. private Notification buildNotification() {
  1084. final String albumName = getAlbumName();
  1085. final String artistName = getArtistName();
  1086. final boolean isPlaying = isPlaying();
  1087. String text = TextUtils.isEmpty(albumName)
  1088. ? artistName : artistName + " - " + albumName;
  1089. int playButtonResId = isPlaying
  1090. ? R.drawable.ic_pause_white_36dp : R.drawable.ic_play_white_36dp;
  1091. Intent nowPlayingIntent = NavigationUtils.getNowPlayingIntent(this);
  1092. PendingIntent clickIntent = PendingIntent.getActivity(this, 0, nowPlayingIntent, PendingIntent.FLAG_UPDATE_CURRENT);
  1093. Bitmap artwork;
  1094. artwork = ImageLoader.getInstance().loadImageSync(TimberUtils.getAlbumArtUri(getAlbumId()).toString());
  1095. if (artwork == null) {
  1096. artwork = ImageLoader.getInstance().loadImageSync("drawable://" + R.drawable.ic_empty_music2);
  1097. }
  1098. if (mNotificationPostTime == 0) {
  1099. mNotificationPostTime = System.currentTimeMillis();
  1100. }
  1101. android.support.v4.app.NotificationCompat.Builder builder = new android.support.v4.app.NotificationCompat.Builder(this, CHANNEL_ID)
  1102. .setSmallIcon(R.drawable.ic_notification)
  1103. .setLargeIcon(artwork)
  1104. .setContentIntent(clickIntent)
  1105. .setContentTitle(getTrackName())
  1106. .setContentText(text)
  1107. .setWhen(mNotificationPostTime)
  1108. .addAction(R.drawable.ic_skip_previous_white_36dp,
  1109. "",
  1110. retrievePlaybackAction(PREVIOUS_ACTION))
  1111. .addAction(playButtonResId, "",
  1112. retrievePlaybackAction(TOGGLEPAUSE_ACTION))
  1113. .addAction(R.drawable.ic_skip_next_white_36dp,
  1114. "",
  1115. retrievePlaybackAction(NEXT_ACTION));
  1116. if (TimberUtils.isJellyBeanMR1()) {
  1117. builder.setShowWhen(false);
  1118. }
  1119. if (TimberUtils.isLollipop()) {
  1120. builder.setVisibility(Notification.VISIBILITY_PUBLIC);
  1121. android.support.v4.media.app.NotificationCompat.MediaStyle style = new android.support.v4.media.app.NotificationCompat.MediaStyle()
  1122. .setMediaSession(mSession.getSessionToken())
  1123. .setShowActionsInCompactView(0, 1, 2, 3);
  1124. builder.setStyle(style);
  1125. }
  1126. if (artwork != null && TimberUtils.isLollip

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