Java tutorial
/* * Copyright (C) 2012 Andrew Neal * Copyright (C) 2014 The CyanogenMod Project * Copyright (C) 2015 Naman Dwivedi * * Licensed under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law * or agreed to in writing, software distributed under the License is * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package com.av.remusic.service; import android.Manifest; import android.annotation.SuppressLint; import android.app.AlarmManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.database.ContentObserver; import android.database.Cursor; import android.database.MatrixCursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.media.AudioManager; import android.media.AudioManager.OnAudioFocusChangeListener; import android.media.MediaMetadata; import android.media.MediaPlayer; import android.media.audiofx.AudioEffect; import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Parcel; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.RemoteException; import android.os.SystemClock; import android.provider.MediaStore; import android.provider.MediaStore.Audio.AlbumColumns; import android.provider.MediaStore.Audio.AudioColumns; import android.support.annotation.Nullable; import android.support.v4.app.NotificationCompat; import android.text.TextUtils; import android.util.Log; import android.widget.RemoteViews; import com.facebook.common.executors.CallerThreadExecutor; import com.facebook.common.references.CloseableReference; import com.facebook.datasource.DataSource; import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.imagepipeline.core.ImagePipeline; import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber; import com.facebook.imagepipeline.image.CloseableImage; import com.facebook.imagepipeline.request.ImageRequest; import com.facebook.imagepipeline.request.ImageRequestBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; import com.av.remusic.MainApplication; import com.av.remusic.MediaAidlInterface; import com.av.remusic.R; import com.av.remusic.activity.LockActivity; import com.av.remusic.downmusic.Down; import com.av.remusic.info.MusicInfo; import com.av.remusic.json.MusicFileDownInfo; import com.av.remusic.net.BMA; import com.av.remusic.net.HttpUtil; import com.av.remusic.permissions.Nammu; import com.av.remusic.provider.MusicPlaybackState; import com.av.remusic.provider.RecentStore; import com.av.remusic.proxy.utils.MediaPlayerProxy; import com.av.remusic.receiver.MediaButtonIntentReceiver; import com.av.remusic.recent.SongPlayCount; import com.av.remusic.uitl.CommonUtils; import com.av.remusic.uitl.ImageUtils; import com.av.remusic.uitl.L; import com.av.remusic.uitl.PreferencesUtility; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.RandomAccessFile; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.ListIterator; import java.util.Map; import java.util.Random; import java.util.TreeSet; @SuppressLint("NewApi") public class MediaService extends Service { public static final String PLAYSTATE_CHANGED = "com.av.remusic.playstatechanged"; public static final String POSITION_CHANGED = "com.av.remusic.positionchanged"; public static final String META_CHANGED = "com.av.remusic.metachanged"; public static final String PLAYLIST_ITEM_MOVED = "com.av.remusic.mmoved"; public static final String QUEUE_CHANGED = "com.av.remusic.queuechanged"; public static final String PLAYLIST_CHANGED = "com.av.remusic.playlistchanged"; public static final String REPEATMODE_CHANGED = "com.av.remusic.repeatmodechanged"; public static final String SHUFFLEMODE_CHANGED = "com.av.remusic.shufflemodechanged"; public static final String TRACK_ERROR = "com.av.remusic.trackerror"; public static final String TIMBER_PACKAGE_NAME = "com.av.remusic"; public static final String MUSIC_PACKAGE_NAME = "com.android.music"; public static final String SERVICECMD = "com.av.remusic.musicservicecommand"; public static final String TOGGLEPAUSE_ACTION = "com.av.remusic.togglepause"; public static final String PAUSE_ACTION = "com.av.remusic.pause"; public static final String STOP_ACTION = "com.av.remusic.stop"; public static final String PREVIOUS_ACTION = "com.av.remusic.previous"; public static final String PREVIOUS_FORCE_ACTION = "com.av.remusic.previous.force"; public static final String NEXT_ACTION = "com.av.remusic.next"; public static final String MUSIC_CHANGED = "com.av.remusi.change_music"; public static final String REPEAT_ACTION = "com.av.remusic.repeat"; public static final String SHUFFLE_ACTION = "com.av.remusic.shuffle"; public static final String FROM_MEDIA_BUTTON = "frommediabutton"; public static final String REFRESH = "com.av.remusic.refresh"; public static final String LRC_UPDATED = "com.av.remusic.updatelrc"; public static final String UPDATE_LOCKSCREEN = "com.av.remusic.updatelockscreen"; public static final String CMDNAME = "command"; public static final String CMDTOGGLEPAUSE = "togglepause"; public static final String CMDSTOP = "stop"; public static final String CMDPAUSE = "pause"; public static final String CMDPLAY = "play"; public static final String CMDPREVIOUS = "previous"; public static final String CMDNEXT = "next"; public static final String CMDNOTIF = "buttonId"; public static final String TRACK_PREPARED = "com.av.remusic.prepared"; public static final String TRY_GET_TRACKINFO = "com.av.remusic.gettrackinfo"; public static final String BUFFER_UP = "com.av.remusic.bufferup"; public static final String LOCK_SCREEN = "com.av.remusic.lock"; public static final String SEND_PROGRESS = "com.av.remusic.progress"; public static final String MUSIC_LODING = "com.av.remusic.loading"; private static final String SHUTDOWN = "com.av.remusic.shutdown"; public static final String SETQUEUE = "com.av.remusic.setqueue"; public static final int NEXT = 2; public static final int LAST = 3; public static final int SHUFFLE_NONE = 0; public static final int SHUFFLE_NORMAL = 1; public static final int SHUFFLE_AUTO = 2; public static final int REPEAT_NONE = 2; public static final int REPEAT_CURRENT = 1; public static final int REPEAT_ALL = 2; public static final int MAX_HISTORY_SIZE = 1000; private static final String TAG = "MusicPlaybackService"; private static final boolean D = true; private static final int LRC_DOWNLOADED = -10; private static final int IDCOLIDX = 0; private static final int TRACK_ENDED = 1; private static final int TRACK_WENT_TO_NEXT = 2; private static final int RELEASE_WAKELOCK = 3; private static final int SERVER_DIED = 4; private static final int FOCUSCHANGE = 5; private static final int FADEDOWN = 6; private static final int FADEUP = 7; private static final int IDLE_DELAY = 5 * 60 * 1000; private static final long REWIND_INSTEAD_PREVIOUS_THRESHOLD = 3000; private static final String[] PROJECTION = new String[] { "audio._id AS _id", MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.ALBUM, MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.DATA, MediaStore.Audio.Media.MIME_TYPE, MediaStore.Audio.Media.ALBUM_ID, MediaStore.Audio.Media.ARTIST_ID }; private static final String[] ALBUM_PROJECTION = new String[] { MediaStore.Audio.Albums.ALBUM, MediaStore.Audio.Albums.ARTIST, MediaStore.Audio.Albums.LAST_YEAR }; private static final Shuffler mShuffler = new Shuffler(); private static final int NOTIFY_MODE_NONE = 0; private static final int NOTIFY_MODE_FOREGROUND = 1; private static final int NOTIFY_MODE_BACKGROUND = 2; private static final String[] PROJECTION_MATRIX = new String[] { "_id", MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.ALBUM, MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.DATA, MediaStore.Audio.Media.MIME_TYPE, MediaStore.Audio.Media.ALBUM_ID, MediaStore.Audio.Media.ARTIST_ID }; private static LinkedList<Integer> mHistory = new LinkedList<>(); private final IBinder mBinder = new ServiceStub(this); private MultiPlayer mPlayer; private String mFileToPlay; private WakeLock mWakeLock; private AlarmManager mAlarmManager; private PendingIntent mShutdownIntent; private boolean mShutdownScheduled; private NotificationManager mNotificationManager; private Cursor mCursor; private Cursor mAlbumCursor; private AudioManager mAudioManager; private SharedPreferences mPreferences; private boolean mServiceInUse = false; private boolean mIsSupposedToBePlaying = false; private long mLastPlayedTime; private int mNotifyMode = NOTIFY_MODE_NONE; private long mNotificationPostTime = 0; private boolean mQueueIsSaveable = true; private boolean mPausedByTransientLossOfFocus = false; private MediaSession mSession; private ComponentName mMediaButtonReceiverComponent; private int mCardId; private int mPlayPos = -1; private int mNextPlayPos = -1; private int mOpenFailedCounter = 0; private int mMediaMountedCount = 0; private int mShuffleMode = SHUFFLE_NONE; private int mRepeatMode = REPEAT_ALL; private int mServiceStartId = -1; private ArrayList<MusicTrack> mPlaylist = new ArrayList<MusicTrack>(100); private HashMap<Long, MusicInfo> mPlaylistInfo = new HashMap<>(); private long[] mAutoShuffleList = null; private MusicPlayerHandler mPlayerHandler; private HandlerThread mHandlerThread; private BroadcastReceiver mUnmountReceiver = null; private MusicPlaybackState mPlaybackStateStore; private boolean mShowAlbumArtOnLockscreen; private SongPlayCount mSongPlayCount; private RecentStore mRecentStore; private int mNotificationId = 1000; private ContentObserver mMediaStoreObserver; private static Handler mUrlHandler; private static Handler mLrcHandler; private MediaPlayerProxy mProxy; public static final String LRC_PATH = "/remusic/lrc/"; private long mLastSeekPos = 0; private RequestPlayUrl mRequestUrl; private RequestLrc mRequestLrc; private boolean mIsSending = false; private boolean mIsLocked; private Bitmap mNoBit; private Notification mNotification; private Thread mLrcThread = new Thread(new Runnable() { @Override public void run() { Looper.prepare(); mLrcHandler = new Handler(); Looper.loop(); } }); private Thread mGetUrlThread = new Thread(new Runnable() { @Override public void run() { Looper.prepare(); mUrlHandler = new Handler(); Looper.loop(); } }); private final OnAudioFocusChangeListener mAudioFocusListener = new OnAudioFocusChangeListener() { @Override public void onAudioFocusChange(final int focusChange) { mPlayerHandler.obtainMessage(FOCUSCHANGE, focusChange, 0).sendToTarget(); } }; private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent intent) { final String command = intent.getStringExtra(CMDNAME); Log.d(TAG, "onreceive" + intent.toURI()); handleCommandIntent(intent); } }; @Override public IBinder onBind(final Intent intent) { if (D) Log.d(TAG, "Service bound, intent = " + intent); cancelShutdown(); mServiceInUse = true; return mBinder; } @Override public boolean onUnbind(final Intent intent) { if (D) Log.d(TAG, "Service unbound"); mServiceInUse = false; saveQueue(true); if (mIsSupposedToBePlaying || mPausedByTransientLossOfFocus) { return true; } else if (mPlaylist.size() > 0 || mPlayerHandler.hasMessages(TRACK_ENDED)) { scheduleDelayedShutdown(); return true; } stopSelf(mServiceStartId); return true; } @Override public void onRebind(final Intent intent) { cancelShutdown(); mServiceInUse = true; } @Override public void onCreate() { if (D) Log.d(TAG, "Creating service"); super.onCreate(); mGetUrlThread.start(); mLrcThread.start(); mProxy = new MediaPlayerProxy(this); mProxy.init(); mProxy.start(); mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); // gets a pointer to the playback state store mPlaybackStateStore = MusicPlaybackState.getInstance(this); mSongPlayCount = SongPlayCount.getInstance(this); mRecentStore = RecentStore.getInstance(this); mHandlerThread = new HandlerThread("MusicPlayerHandler", android.os.Process.THREAD_PRIORITY_BACKGROUND); mHandlerThread.start(); mPlayerHandler = new MusicPlayerHandler(this, mHandlerThread.getLooper()); mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); mMediaButtonReceiverComponent = new ComponentName(getPackageName(), MediaButtonIntentReceiver.class.getName()); mAudioManager.registerMediaButtonEventReceiver(mMediaButtonReceiverComponent); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { setUpMediaSession(); } mPreferences = getSharedPreferences("Service", 0); mCardId = getCardId(); registerExternalStorageListener(); mPlayer = new MultiPlayer(this); mPlayer.setHandler(mPlayerHandler); // Initialize the intent filter and each action final IntentFilter filter = new IntentFilter(); filter.addAction(SERVICECMD); filter.addAction(TOGGLEPAUSE_ACTION); filter.addAction(PAUSE_ACTION); filter.addAction(STOP_ACTION); filter.addAction(NEXT_ACTION); filter.addAction(PREVIOUS_ACTION); filter.addAction(PREVIOUS_FORCE_ACTION); filter.addAction(REPEAT_ACTION); filter.addAction(SHUFFLE_ACTION); filter.addAction(TRY_GET_TRACKINFO); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(LOCK_SCREEN); filter.addAction(SEND_PROGRESS); filter.addAction(SETQUEUE); // Attach the broadcast listener registerReceiver(mIntentReceiver, filter); mMediaStoreObserver = new MediaStoreObserver(mPlayerHandler); getContentResolver().registerContentObserver(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, true, mMediaStoreObserver); getContentResolver().registerContentObserver(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, true, mMediaStoreObserver); // Initialize the wake lock final PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName()); mWakeLock.setReferenceCounted(false); final Intent shutdownIntent = new Intent(this, MediaService.class); shutdownIntent.setAction(SHUTDOWN); mAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); mShutdownIntent = PendingIntent.getService(this, 0, shutdownIntent, 0); scheduleDelayedShutdown(); reloadQueueAfterPermissionCheck(); notifyChange(QUEUE_CHANGED); notifyChange(META_CHANGED); } private void setUpMediaSession() { mSession = new MediaSession(this, "remusic"); mSession.setCallback(new MediaSession.Callback() { @Override public void onPause() { pause(); mPausedByTransientLossOfFocus = false; } @Override public void onPlay() { play(); } @Override public void onSeekTo(long pos) { seek(pos); } @Override public void onSkipToNext() { gotoNext(true); } @Override public void onSkipToPrevious() { prev(false); } @Override public void onStop() { pause(); mPausedByTransientLossOfFocus = false; seek(0); releaseServiceUiAndStop(); } }); mSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS); } public void exit() { } @Override public void onDestroy() { if (D) Log.d(TAG, "Destroying service"); super.onDestroy(); // Remove any sound effects final Intent audioEffectsIntent = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION); audioEffectsIntent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, getAudioSessionId()); audioEffectsIntent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName()); sendBroadcast(audioEffectsIntent); cancelNotification(); mAlarmManager.cancel(mShutdownIntent); mPlayerHandler.removeCallbacksAndMessages(null); if (CommonUtils.isJellyBeanMR2()) mHandlerThread.quitSafely(); else mHandlerThread.quit(); mPlayer.release(); mPlayer = null; mAudioManager.abandonAudioFocus(mAudioFocusListener); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) mSession.release(); getContentResolver().unregisterContentObserver(mMediaStoreObserver); closeCursor(); unregisterReceiver(mIntentReceiver); if (mUnmountReceiver != null) { unregisterReceiver(mUnmountReceiver); mUnmountReceiver = null; } mWakeLock.release(); } @Override public int onStartCommand(final Intent intent, final int flags, final int startId) { if (D) Log.d(TAG, "Got new intent " + intent + ", startId = " + startId); mServiceStartId = startId; if (intent != null) { final String action = intent.getAction(); if (SHUTDOWN.equals(action)) { mShutdownScheduled = false; releaseServiceUiAndStop(); return START_NOT_STICKY; } handleCommandIntent(intent); } scheduleDelayedShutdown(); if (intent != null && intent.getBooleanExtra(FROM_MEDIA_BUTTON, false)) { MediaButtonIntentReceiver.completeWakefulIntent(intent); } return START_STICKY; } private void releaseServiceUiAndStop() { if (isPlaying() || mPausedByTransientLossOfFocus || mPlayerHandler.hasMessages(TRACK_ENDED)) { return; } if (D) Log.d(TAG, "Nothing is playing anymore, releasing notification"); cancelNotification(); mAudioManager.abandonAudioFocus(mAudioFocusListener); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) mSession.setActive(false); if (!mServiceInUse) { saveQueue(true); stopSelf(mServiceStartId); } } private void handleCommandIntent(Intent intent) { final String action = intent.getAction(); final String command = SERVICECMD.equals(action) ? intent.getStringExtra(CMDNAME) : null; if (D) Log.d(TAG, "handleCommandIntent: action = " + action + ", command = " + command); if (CMDNEXT.equals(command) || NEXT_ACTION.equals(action)) { gotoNext(true); } else if (CMDPREVIOUS.equals(command) || PREVIOUS_ACTION.equals(action) || PREVIOUS_FORCE_ACTION.equals(action)) { prev(PREVIOUS_FORCE_ACTION.equals(action)); } else if (CMDTOGGLEPAUSE.equals(command) || TOGGLEPAUSE_ACTION.equals(action)) { if (isPlaying()) { pause(); mPausedByTransientLossOfFocus = false; } else { play(); } } else if (CMDPAUSE.equals(command) || PAUSE_ACTION.equals(action)) { pause(); mPausedByTransientLossOfFocus = false; } else if (CMDPLAY.equals(command)) { play(); } else if (CMDSTOP.equals(command) || STOP_ACTION.equals(action)) { pause(); mPausedByTransientLossOfFocus = false; seek(0); releaseServiceUiAndStop(); } else if (REPEAT_ACTION.equals(action)) { cycleRepeat(); } else if (SHUFFLE_ACTION.equals(action)) { cycleShuffle(); } else if (TRY_GET_TRACKINFO.equals(action)) { getLrc(mPlaylist.get(mPlayPos).mId); } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { if (isPlaying() && !mIsLocked) { Intent lockscreen = new Intent(this, LockActivity.class); lockscreen.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(lockscreen); } } else if (LOCK_SCREEN.equals(action)) { mIsLocked = intent.getBooleanExtra("islock", true); L.D(D, TAG, "isloced = " + mIsLocked); } else if (SEND_PROGRESS.equals(action)) { if (isPlaying() && !mIsSending) { mPlayerHandler.post(sendDuration); mIsSending = true; } else if (!isPlaying()) { mPlayerHandler.removeCallbacks(sendDuration); mIsSending = false; } } else if (SETQUEUE.equals(action)) { Log.e("playab", "action"); setQueuePosition(intent.getIntExtra("position", 0)); } } private Runnable sendDuration = new Runnable() { @Override public void run() { notifyChange(SEND_PROGRESS); mPlayerHandler.postDelayed(sendDuration, 1000); } }; private void updateNotification() { final int newNotifyMode; if (isPlaying()) { newNotifyMode = NOTIFY_MODE_FOREGROUND; } else if (recentlyPlayed()) { newNotifyMode = NOTIFY_MODE_BACKGROUND; } else { newNotifyMode = NOTIFY_MODE_NONE; } // int mNotificationId = hashCode(); if (mNotifyMode != newNotifyMode) { if (mNotifyMode == NOTIFY_MODE_FOREGROUND) { if (CommonUtils.isLollipop()) stopForeground(newNotifyMode == NOTIFY_MODE_NONE); else stopForeground(newNotifyMode == NOTIFY_MODE_NONE || newNotifyMode == NOTIFY_MODE_BACKGROUND); } else if (newNotifyMode == NOTIFY_MODE_NONE) { mNotificationManager.cancel(mNotificationId); mNotificationPostTime = 0; } } if (newNotifyMode == NOTIFY_MODE_FOREGROUND) { startForeground(mNotificationId, getNotification()); } else if (newNotifyMode == NOTIFY_MODE_BACKGROUND) { mNotificationManager.notify(mNotificationId, getNotification()); } mNotifyMode = newNotifyMode; } private void cancelNotification() { stopForeground(true); //mNotificationManager.cancel(hashCode()); mNotificationManager.cancel(mNotificationId); mNotificationPostTime = 0; mNotifyMode = NOTIFY_MODE_NONE; } private int getCardId() { if (CommonUtils.isMarshmallow()) { if (Nammu.checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE)) { return getmCardId(); } else return 0; } else { return getmCardId(); } } private int getmCardId() { final ContentResolver resolver = getContentResolver(); Cursor cursor = resolver.query(Uri.parse("content://media/external/fs_id"), null, null, null, null); int mCardId = -1; if (cursor != null && cursor.moveToFirst()) { mCardId = cursor.getInt(0); cursor.close(); } return mCardId; } public void closeExternalStorageFiles(final String storagePath) { stop(true); notifyChange(QUEUE_CHANGED); notifyChange(META_CHANGED); } public void registerExternalStorageListener() { if (mUnmountReceiver == null) { mUnmountReceiver = new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent intent) { final String action = intent.getAction(); if (action.equals(Intent.ACTION_MEDIA_EJECT)) { saveQueue(true); mQueueIsSaveable = false; closeExternalStorageFiles(intent.getData().getPath()); } else if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) { mMediaMountedCount++; mCardId = getCardId(); reloadQueueAfterPermissionCheck(); mQueueIsSaveable = true; notifyChange(QUEUE_CHANGED); notifyChange(META_CHANGED); } } }; final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_MEDIA_EJECT); filter.addAction(Intent.ACTION_MEDIA_MOUNTED); filter.addDataScheme("file"); registerReceiver(mUnmountReceiver, filter); } } private void scheduleDelayedShutdown() { if (D) Log.v(TAG, "Scheduling shutdown in " + IDLE_DELAY + " ms"); mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + IDLE_DELAY, mShutdownIntent); mShutdownScheduled = true; } private void cancelShutdown() { if (D) Log.d(TAG, "Cancelling delayed shutdown, scheduled = " + mShutdownScheduled); if (mShutdownScheduled) { mAlarmManager.cancel(mShutdownIntent); mShutdownScheduled = false; } } private void stop(final boolean goToIdle) { if (D) Log.d(TAG, "Stopping playback, goToIdle = " + goToIdle); if (mPlayer.isInitialized()) { mPlayer.stop(); } mFileToPlay = null; closeCursor(); if (goToIdle) { setIsSupposedToBePlaying(false, false); } // else { // if (CommonUtils.isLollipop()) // stopForeground(false); // else stopForeground(true); // } } private int removeTracksInternal(int first, int last) { synchronized (this) { if (last < first) { return 0; } else if (first < 0) { first = 0; } else if (last >= mPlaylist.size()) { last = mPlaylist.size() - 1; } boolean gotonext = false; if (first <= mPlayPos && mPlayPos <= last) { mPlayPos = first; gotonext = true; } else if (mPlayPos > last) { mPlayPos -= last - first + 1; } final int numToRemove = last - first + 1; if (first == 0 && last == mPlaylist.size() - 1) { mPlayPos = -1; mNextPlayPos = -1; mPlaylist.clear(); mHistory.clear(); } else { for (int i = 0; i < numToRemove; i++) { mPlaylistInfo.remove(mPlaylist.get(first).mId); mPlaylist.remove(first); } ListIterator<Integer> positionIterator = mHistory.listIterator(); while (positionIterator.hasNext()) { int pos = positionIterator.next(); if (pos >= first && pos <= last) { positionIterator.remove(); } else if (pos > last) { positionIterator.set(pos - numToRemove); } } } if (gotonext) { if (mPlaylist.size() == 0) { stop(true); mPlayPos = -1; closeCursor(); } else { if (mShuffleMode != SHUFFLE_NONE) { mPlayPos = getNextPosition(true); } else if (mPlayPos >= mPlaylist.size()) { mPlayPos = 0; } final boolean wasPlaying = isPlaying(); stop(false); openCurrentAndNext(); if (wasPlaying) { play(); } } notifyChange(META_CHANGED); } return last - first + 1; } } private void addToPlayList(final long[] list, int position) { final int addlen = list.length; if (position < 0) { mPlaylist.clear(); position = 0; } mPlaylist.ensureCapacity(mPlaylist.size() + addlen); if (position > mPlaylist.size()) { position = mPlaylist.size(); } final ArrayList<MusicTrack> arrayList = new ArrayList<MusicTrack>(addlen); for (int i = 0; i < list.length; i++) { arrayList.add(new MusicTrack(list[i], i)); } mPlaylist.addAll(position, arrayList); if (mPlaylist.size() == 0) { closeCursor(); notifyChange(META_CHANGED); } } private void updateCursor(final long trackId) { MusicInfo info = mPlaylistInfo.get(trackId); if (mPlaylistInfo.get(trackId) != null) { MatrixCursor cursor = new MatrixCursor(PROJECTION); cursor.addRow(new Object[] { info.songId, info.artist, info.albumName, info.musicName, info.data, info.albumData, info.albumId, info.artistId }); cursor.moveToFirst(); mCursor = cursor; cursor.close(); } } private void updateCursor(final String selection, final String[] selectionArgs) { synchronized (this) { closeCursor(); mCursor = openCursorAndGoToFirst(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, PROJECTION, selection, selectionArgs); } } private void updateCursor(final Uri uri) { synchronized (this) { closeCursor(); mCursor = openCursorAndGoToFirst(uri, PROJECTION, null, null); } } private Cursor openCursorAndGoToFirst(Uri uri, String[] projection, String selection, String[] selectionArgs) { Cursor c = getContentResolver().query(uri, projection, selection, selectionArgs, null); if (c == null) { return null; } if (!c.moveToFirst()) { c.close(); return null; } return c; } private synchronized void closeCursor() { if (mCursor != null) { mCursor.close(); mCursor = null; } if (mAlbumCursor != null) { mAlbumCursor.close(); mAlbumCursor = null; } } class RequestPlayUrl implements Runnable { private long id; private boolean play; private boolean stop; public RequestPlayUrl(long id, boolean play) { this.id = id; this.play = play; } public void stop() { stop = true; } @Override public void run() { try { String url = PreferencesUtility.getInstance(MediaService.this).getPlayLink(id); if (url == null) { MusicFileDownInfo song = Down.getUrl(MediaService.this, id + ""); if (song != null && song.getShow_link() != null) { url = song.getShow_link(); PreferencesUtility.getInstance(MediaService.this).setPlayLink(id, url); } } if (url != null) { L.E(D, TAG, "current url = " + url); } else { gotoNext(true); } if (!stop) { startProxy(); // String urlEn = HttpUtil.urlEncode(url); String urlEn = url; urlEn = mProxy.getProxyURL(urlEn); mPlayer.setDataSource(urlEn); } if (play && !stop) { play(); } } catch (Exception e) { e.printStackTrace(); } } } class RequestLrc implements Runnable { private MusicInfo musicInfo; private boolean stop; RequestLrc(MusicInfo info) { this.musicInfo = info; } public void stop() { stop = true; } @Override public void run() { L.E(D, TAG, "start to getlrc"); String url = null; if (musicInfo != null && musicInfo.lrc != null) { url = musicInfo.lrc; } try { JsonObject jsonObject = HttpUtil .getResposeJsonObject(BMA.Search.searchLrcPic(musicInfo.musicName, musicInfo.artist)); JsonArray array = jsonObject.get("songinfo").getAsJsonArray(); int len = array.size(); url = null; for (int i = 0; i < len; i++) { url = array.get(i).getAsJsonObject().get("lrclink").getAsString(); if (url != null) { L.D(D, TAG, "lrclink = " + url); break; } } } catch (Exception e) { e.printStackTrace(); } if (!stop) { File file = new File( Environment.getExternalStorageDirectory().getAbsolutePath() + LRC_PATH + musicInfo.songId); String lrc = null; try { lrc = HttpUtil.getResposeString(url); if (lrc != null && !lrc.isEmpty()) { if (!file.exists()) file.createNewFile(); writeToFile(file, lrc); mPlayerHandler.sendEmptyMessage(LRC_DOWNLOADED); } } catch (Exception e) { e.printStackTrace(); } } } } private void getLrc(long id) { MusicInfo info = mPlaylistInfo.get(id); if (info == null) { L.D(D, TAG, "get lrc err ,musicinfo is null"); } String lrc = Environment.getExternalStorageDirectory().getAbsolutePath() + LRC_PATH; File file = new File(lrc); L.D(D, TAG, "file exists = " + file.exists()); if (!file.exists()) { //? boolean r = file.mkdirs(); L.D(D, TAG, "file created = " + r); } file = new File(lrc + id); if (!file.exists()) { if (mRequestLrc != null) { mRequestLrc.stop(); mLrcHandler.removeCallbacks(mRequestLrc); } mRequestLrc = new RequestLrc(mPlaylistInfo.get(id)); mLrcHandler.postDelayed(mRequestLrc, 70); } } private synchronized void writeToFile(File file, String lrc) { try { FileOutputStream outputStream = new FileOutputStream(file); outputStream.write(lrc.getBytes()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private void startProxy() { if (mProxy == null) { mProxy = new MediaPlayerProxy(this); mProxy.init(); mProxy.start(); } } private void openCurrentAndNextPlay(boolean play) { openCurrentAndMaybeNext(play, true); } private void openCurrentAndNext() { openCurrentAndMaybeNext(false, true); } private void openCurrentAndMaybeNext(final boolean play, final boolean openNext) { synchronized (this) { if (D) Log.d(TAG, "open current"); closeCursor(); stop(false); boolean shutdown = false; if (mPlaylist.size() == 0 || mPlaylistInfo.size() == 0 && mPlayPos >= mPlaylist.size()) { clearPlayInfos(); return; } final long id = mPlaylist.get(mPlayPos).mId; updateCursor(id); getLrc(id); if (mPlaylistInfo.get(id) == null) { return; } if (!mPlaylistInfo.get(id).islocal) { if (mRequestUrl != null) { mRequestUrl.stop(); mUrlHandler.removeCallbacks(mRequestUrl); } mRequestUrl = new RequestPlayUrl(id, play); mUrlHandler.postDelayed(mRequestUrl, 70); } else { while (true) { if (mCursor != null && openFile( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "/" + mCursor.getLong(IDCOLIDX))) { break; } closeCursor(); if (mOpenFailedCounter++ < 10 && mPlaylist.size() > 1) { final int pos = getNextPosition(false); if (pos < 0) { shutdown = true; break; } mPlayPos = pos; stop(false); mPlayPos = pos; updateCursor(mPlaylist.get(mPlayPos).mId); } else { mOpenFailedCounter = 0; Log.w(TAG, "Failed to open file for playback"); shutdown = true; break; } } } if (shutdown) { scheduleDelayedShutdown(); if (mIsSupposedToBePlaying) { mIsSupposedToBePlaying = false; notifyChange(PLAYSTATE_CHANGED); } } else if (openNext) { setNextTrack(); } } } private void sendErrorMessage(final String trackName) { final Intent i = new Intent(TRACK_ERROR); i.putExtra(TrackErrorExtra.TRACK_NAME, trackName); sendBroadcast(i); } private int getNextPosition(final boolean force) { if (mPlaylist == null || mPlaylist.isEmpty()) { return -1; } if (!force && mRepeatMode == REPEAT_CURRENT) { if (mPlayPos < 0) { return 0; } return mPlayPos; } else if (mShuffleMode == SHUFFLE_NORMAL) { final int numTracks = mPlaylist.size(); final int[] trackNumPlays = new int[numTracks]; for (int i = 0; i < numTracks; i++) { trackNumPlays[i] = 0; } final int numHistory = mHistory.size(); for (int i = 0; i < numHistory; i++) { final int idx = mHistory.get(i).intValue(); if (idx >= 0 && idx < numTracks) { trackNumPlays[idx]++; } } if (mPlayPos >= 0 && mPlayPos < numTracks) { trackNumPlays[mPlayPos]++; } int minNumPlays = Integer.MAX_VALUE; int numTracksWithMinNumPlays = 0; for (int i = 0; i < trackNumPlays.length; i++) { if (trackNumPlays[i] < minNumPlays) { minNumPlays = trackNumPlays[i]; numTracksWithMinNumPlays = 1; } else if (trackNumPlays[i] == minNumPlays) { numTracksWithMinNumPlays++; } } if (minNumPlays > 0 && numTracksWithMinNumPlays == numTracks && mRepeatMode != REPEAT_ALL && !force) { return -1; } int skip = mShuffler.nextInt(numTracksWithMinNumPlays); for (int i = 0; i < trackNumPlays.length; i++) { if (trackNumPlays[i] == minNumPlays) { if (skip == 0) { return i; } else { skip--; } } } if (D) Log.e(TAG, "Getting the next position resulted did not get a result when it should have"); return -1; } else if (mShuffleMode == SHUFFLE_AUTO) { doAutoShuffleUpdate(); return mPlayPos + 1; } else { if (mPlayPos >= mPlaylist.size() - 1) { if (mRepeatMode == REPEAT_NONE && !force) { return -1; } else if (mRepeatMode == REPEAT_ALL || force) { return 0; } return -1; } else { return mPlayPos + 1; } } } private void setNextTrack() { setNextTrack(getNextPosition(false)); } private void setNextTrack(int position) { mNextPlayPos = position; if (D) Log.d(TAG, "setNextTrack: next play position = " + mNextPlayPos); if (mNextPlayPos >= 0 && mPlaylist != null && mNextPlayPos < mPlaylist.size()) { final long id = mPlaylist.get(mNextPlayPos).mId; if (mPlaylistInfo.get(id) != null) { if (mPlaylistInfo.get(id).islocal) { mPlayer.setNextDataSource(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "/" + id); } else { mPlayer.setNextDataSource(null); } } } else { mPlayer.setNextDataSource(null); } } private boolean makeAutoShuffleList() { Cursor cursor = null; try { cursor = getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, new String[] { MediaStore.Audio.Media._ID }, MediaStore.Audio.Media.IS_MUSIC + "=1", null, null); if (cursor == null || cursor.getCount() == 0) { return false; } final int len = cursor.getCount(); final long[] list = new long[len]; for (int i = 0; i < len; i++) { cursor.moveToNext(); list[i] = cursor.getLong(0); } mAutoShuffleList = list; return true; } catch (final RuntimeException e) { } finally { if (cursor != null) { cursor.close(); cursor = null; } } return false; } private void doAutoShuffleUpdate() { boolean notify = false; if (mPlayPos > 10) { removeTracks(0, mPlayPos - 9); notify = true; } final int toAdd = 7 - (mPlaylist.size() - (mPlayPos < 0 ? -1 : mPlayPos)); for (int i = 0; i < toAdd; i++) { int lookback = mHistory.size(); int idx = -1; while (true) { idx = mShuffler.nextInt(mAutoShuffleList.length); if (!wasRecentlyUsed(idx, lookback)) { break; } lookback /= 2; } mHistory.add(idx); if (mHistory.size() > MAX_HISTORY_SIZE) { mHistory.remove(0); } mPlaylist.add(new MusicTrack(mAutoShuffleList[idx], -1)); notify = true; } if (notify) { notifyChange(QUEUE_CHANGED); } } private boolean wasRecentlyUsed(final int idx, int lookbacksize) { if (lookbacksize == 0) { return false; } final int histsize = mHistory.size(); if (histsize < lookbacksize) { lookbacksize = histsize; } final int maxidx = histsize - 1; for (int i = 0; i < lookbacksize; i++) { final long entry = mHistory.get(maxidx - i); if (entry == idx) { return true; } } return false; } private void sendUpdateBuffer(int progress) { Intent intent = new Intent(BUFFER_UP); intent.putExtra("progress", progress); sendBroadcast(intent); } private void notifyChange(final String what) { if (D) Log.d(TAG, "notifyChange: what = " + what); if (SEND_PROGRESS.equals(what)) { final Intent intent = new Intent(what); intent.putExtra("position", position()); intent.putExtra("duration", duration()); sendStickyBroadcast(intent); return; } // Update the lockscreen controls if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) updateMediaSession(what); if (what.equals(POSITION_CHANGED)) { return; } final Intent intent = new Intent(what); intent.putExtra("id", getAudioId()); intent.putExtra("artist", getArtistName()); intent.putExtra("album", getAlbumName()); intent.putExtra("track", getTrackName()); intent.putExtra("playing", isPlaying()); intent.putExtra("albumuri", getAlbumPath()); intent.putExtra("islocal", isTrackLocal()); sendStickyBroadcast(intent); final Intent musicIntent = new Intent(intent); musicIntent.setAction(what.replace(TIMBER_PACKAGE_NAME, MUSIC_PACKAGE_NAME)); sendStickyBroadcast(musicIntent); // if (what.equals(TRACK_PREPARED)) { // return; // } if (what.equals(META_CHANGED)) { mRecentStore.addSongId(getAudioId()); mSongPlayCount.bumpSongCount(getAudioId()); } else if (what.equals(QUEUE_CHANGED)) { Intent intent1 = new Intent("com.av.remusic.emptyplaylist"); intent.putExtra("showorhide", "show"); sendBroadcast(intent1); saveQueue(true); if (isPlaying()) { if (mNextPlayPos >= 0 && mNextPlayPos < mPlaylist.size() && getShuffleMode() != SHUFFLE_NONE) { setNextTrack(mNextPlayPos); } else { setNextTrack(); } } } else { saveQueue(false); } if (what.equals(PLAYSTATE_CHANGED)) { updateNotification(); } } private void updateMediaSession(final String what) { int playState = mIsSupposedToBePlaying ? PlaybackState.STATE_PLAYING : PlaybackState.STATE_PAUSED; if (what.equals(PLAYSTATE_CHANGED) || what.equals(POSITION_CHANGED)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mSession.setPlaybackState(new PlaybackState.Builder().setState(playState, position(), 1.0f) .setActions(PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY_PAUSE | PlaybackState.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_SKIP_TO_PREVIOUS) .build()); } } else if (what.equals(META_CHANGED) || what.equals(QUEUE_CHANGED)) { //Bitmap albumArt = ImageLoader.getInstance().loadImageSync(CommonUtils.getAlbumArtUri(getAlbumId()).toString()); Bitmap albumArt = null; if (albumArt != null) { Bitmap.Config config = albumArt.getConfig(); if (config == null) { config = Bitmap.Config.ARGB_8888; } albumArt = albumArt.copy(config, false); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mSession.setMetadata( new MediaMetadata.Builder().putString(MediaMetadata.METADATA_KEY_ARTIST, getArtistName()) .putString(MediaMetadata.METADATA_KEY_ALBUM_ARTIST, getAlbumArtistName()) .putString(MediaMetadata.METADATA_KEY_ALBUM, getAlbumName()) .putString(MediaMetadata.METADATA_KEY_TITLE, getTrackName()) .putLong(MediaMetadata.METADATA_KEY_DURATION, duration()) .putLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER, getQueuePosition() + 1) .putLong(MediaMetadata.METADATA_KEY_NUM_TRACKS, getQueue().length) .putString(MediaMetadata.METADATA_KEY_GENRE, getGenreName()) .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, mShowAlbumArtOnLockscreen ? albumArt : null) .build()); mSession.setPlaybackState(new PlaybackState.Builder().setState(playState, position(), 1.0f) .setActions(PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY_PAUSE | PlaybackState.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_SKIP_TO_PREVIOUS) .build()); } } } private Notification getNotification() { RemoteViews remoteViews; final int PAUSE_FLAG = 0x1; final int NEXT_FLAG = 0x2; final int STOP_FLAG = 0x3; final String albumName = getAlbumName(); final String artistName = getArtistName(); final boolean isPlaying = isPlaying(); remoteViews = new RemoteViews(this.getPackageName(), R.layout.notification); String text = TextUtils.isEmpty(albumName) ? artistName : artistName + " - " + albumName; remoteViews.setTextViewText(R.id.title, getTrackName()); remoteViews.setTextViewText(R.id.text, text); //action? ?flag?? Intent pauseIntent = new Intent(TOGGLEPAUSE_ACTION); pauseIntent.putExtra("FLAG", PAUSE_FLAG); PendingIntent pausePIntent = PendingIntent.getBroadcast(this, 0, pauseIntent, 0); remoteViews.setImageViewResource(R.id.iv_pause, isPlaying ? R.drawable.note_btn_pause : R.drawable.note_btn_play); remoteViews.setOnClickPendingIntent(R.id.iv_pause, pausePIntent); Intent nextIntent = new Intent(NEXT_ACTION); nextIntent.putExtra("FLAG", NEXT_FLAG); PendingIntent nextPIntent = PendingIntent.getBroadcast(this, 0, nextIntent, 0); remoteViews.setOnClickPendingIntent(R.id.iv_next, nextPIntent); Intent preIntent = new Intent(STOP_ACTION); preIntent.putExtra("FLAG", STOP_FLAG); PendingIntent prePIntent = PendingIntent.getBroadcast(this, 0, preIntent, 0); remoteViews.setOnClickPendingIntent(R.id.iv_stop, prePIntent); // PendingIntent pendingIntent = PendingIntent.getActivity(this.getApplicationContext(), 0, // new Intent(this.getApplicationContext(), PlayingActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); final Intent nowPlayingIntent = new Intent(); //nowPlayingIntent.setAction("com.av.remusic.LAUNCH_NOW_PLAYING_ACTION"); nowPlayingIntent .setComponent(new ComponentName("com.av.remusic", "com.av.remusic.activity.PlayingActivity")); nowPlayingIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); PendingIntent clickIntent = PendingIntent.getBroadcast(this, 0, nowPlayingIntent, PendingIntent.FLAG_UPDATE_CURRENT); PendingIntent click = PendingIntent.getActivity(this, 0, nowPlayingIntent, PendingIntent.FLAG_UPDATE_CURRENT); final Bitmap bitmap = ImageUtils.getArtworkQuick(this, getAlbumId(), 160, 160); if (bitmap != null) { remoteViews.setImageViewBitmap(R.id.image, bitmap); // remoteViews.setImageViewUri(R.id.image, MusicUtils.getAlbumUri(this, getAudioId())); mNoBit = null; } else if (!isTrackLocal()) { if (mNoBit != null) { remoteViews.setImageViewBitmap(R.id.image, mNoBit); mNoBit = null; } else { Uri uri = null; if (getAlbumPath() != null) { try { uri = Uri.parse(getAlbumPath()); } catch (Exception e) { e.printStackTrace(); } } if (getAlbumPath() == null || uri == null) { mNoBit = BitmapFactory.decodeResource(getResources(), R.drawable.placeholder_disk_210); updateNotification(); } else { ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(uri) .setProgressiveRenderingEnabled(true).build(); ImagePipeline imagePipeline = Fresco.getImagePipeline(); DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline .fetchDecodedImage(imageRequest, MediaService.this); dataSource.subscribe(new BaseBitmapDataSubscriber() { @Override public void onNewResultImpl(@Nullable Bitmap bitmap) { // You can use the bitmap in only limited ways // No need to do any cleanup. if (bitmap != null) { mNoBit = bitmap; } updateNotification(); } @Override public void onFailureImpl(DataSource dataSource) { // No cleanup required here. mNoBit = BitmapFactory.decodeResource(getResources(), R.drawable.placeholder_disk_210); updateNotification(); } }, CallerThreadExecutor.getInstance()); } } } else { remoteViews.setImageViewResource(R.id.image, R.drawable.placeholder_disk_210); } if (mNotificationPostTime == 0) { mNotificationPostTime = System.currentTimeMillis(); } if (mNotification == null) { NotificationCompat.Builder builder = new NotificationCompat.Builder(this).setContent(remoteViews) .setSmallIcon(R.drawable.ic_notification).setContentIntent(click) .setWhen(mNotificationPostTime); if (CommonUtils.isJellyBeanMR1()) { builder.setShowWhen(false); } mNotification = builder.build(); } else { mNotification.contentView = remoteViews; } return mNotification; } private final PendingIntent retrievePlaybackAction(final String action) { final ComponentName serviceName = new ComponentName(this, MediaService.class); Intent intent = new Intent(action); intent.setComponent(serviceName); return PendingIntent.getService(this, 0, intent, 0); } private void saveQueue(final boolean full) { if (!mQueueIsSaveable) { return; } final SharedPreferences.Editor editor = mPreferences.edit(); if (full) { mPlaybackStateStore.saveState(mPlaylist, mShuffleMode != SHUFFLE_NONE ? mHistory : null); if (mPlaylistInfo.size() > 0) { String temp = MainApplication.gsonInstance().toJson(mPlaylistInfo); try { File file = new File(getCacheDir().getAbsolutePath() + "playlist"); RandomAccessFile ra = new RandomAccessFile(file, "rws"); ra.write(temp.getBytes()); ra.close(); } catch (Exception e) { e.printStackTrace(); } } editor.putInt("cardid", mCardId); } editor.putInt("curpos", mPlayPos); if (mPlayer.isInitialized()) { editor.putLong("seekpos", mPlayer.position()); } editor.putInt("repeatmode", mRepeatMode); editor.putInt("shufflemode", mShuffleMode); editor.apply(); } private void reloadQueueAfterPermissionCheck() { if (CommonUtils.isMarshmallow()) { if (Nammu.checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE)) { reloadQueue(); } } else { reloadQueue(); } } private String readTextFromSDcard(InputStream is) throws Exception { InputStreamReader reader = new InputStreamReader(is); BufferedReader bufferedReader = new BufferedReader(reader); StringBuffer buffer = new StringBuffer(); String str; while ((str = bufferedReader.readLine()) != null) { buffer.append(str); buffer.append("\n"); } return buffer.toString(); } private void clearPlayInfos() { File file = new File(getCacheDir().getAbsolutePath() + "playlist"); if (file.exists()) { file.delete(); } MusicPlaybackState.getInstance(this).clearQueue(); } private void reloadQueue() { int id = mCardId; if (mPreferences.contains("cardid")) { id = mPreferences.getInt("cardid", ~mCardId); } if (id == mCardId) { mPlaylist = mPlaybackStateStore.getQueue(); try { FileInputStream in = new FileInputStream(new File(getCacheDir().getAbsolutePath() + "playlist")); String c = readTextFromSDcard(in); HashMap<Long, MusicInfo> play = MainApplication.gsonInstance().fromJson(c, new TypeToken<HashMap<Long, MusicInfo>>() { }.getType()); if (play != null && play.size() > 0) { mPlaylistInfo = play; L.D(D, TAG, mPlaylistInfo.keySet().toString()); } } catch (Exception e) { e.printStackTrace(); } } if ((mPlaylist.size() == mPlaylistInfo.size()) && mPlaylist.size() > 0) { final int pos = mPreferences.getInt("curpos", 0); if (pos < 0 || pos >= mPlaylist.size()) { mPlaylist.clear(); return; } mPlayPos = pos; updateCursor(mPlaylist.get(mPlayPos).mId); if (mCursor == null) { SystemClock.sleep(3000); updateCursor(mPlaylist.get(mPlayPos).mId); } synchronized (this) { closeCursor(); mOpenFailedCounter = 20; openCurrentAndNext(); } // if (!mPlayer.isInitialized() && isTrackLocal()) { // mPlaylist.clear(); // return; // } final long seekpos = mPreferences.getLong("seekpos", 0); mLastSeekPos = seekpos; seek(seekpos >= 0 && seekpos < duration() ? seekpos : 0); if (D) { Log.d(TAG, "restored queue, currently at position " + position() + "/" + duration() + " (requested " + seekpos + ")"); } int repmode = mPreferences.getInt("repeatmode", REPEAT_ALL); if (repmode != REPEAT_ALL && repmode != REPEAT_CURRENT) { repmode = REPEAT_NONE; } mRepeatMode = repmode; int shufmode = mPreferences.getInt("shufflemode", SHUFFLE_NONE); if (shufmode != SHUFFLE_AUTO && shufmode != SHUFFLE_NORMAL) { shufmode = SHUFFLE_NONE; } if (shufmode != SHUFFLE_NONE) { mHistory = mPlaybackStateStore.getHistory(mPlaylist.size()); } if (shufmode == SHUFFLE_AUTO) { if (!makeAutoShuffleList()) { shufmode = SHUFFLE_NONE; } } mShuffleMode = shufmode; } else { clearPlayInfos(); } notifyChange(MUSIC_CHANGED); } public boolean openFile(final String path) { if (D) Log.d(TAG, "openFile: path = " + path); synchronized (this) { if (path == null) { return false; } if (mCursor == null) { Uri uri = Uri.parse(path); boolean shouldAddToPlaylist = true; long id = -1; try { id = Long.valueOf(uri.getLastPathSegment()); } catch (NumberFormatException ex) { // Ignore } if (id != -1 && path.startsWith(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI.toString())) { updateCursor(uri); } else if (id != -1 && path.startsWith(MediaStore.Files.getContentUri("external").toString())) { updateCursor(id); } else if (path.startsWith("content://downloads/")) { String mpUri = getValueForDownloadedFile(this, uri, "mediaprovider_uri"); if (D) Log.i(TAG, "Downloaded file's MP uri : " + mpUri); if (!TextUtils.isEmpty(mpUri)) { if (openFile(mpUri)) { notifyChange(META_CHANGED); return true; } else { return false; } } else { updateCursorForDownloadedFile(this, uri); shouldAddToPlaylist = false; } } else { String where = MediaStore.Audio.Media.DATA + "=?"; String[] selectionArgs = new String[] { path }; updateCursor(where, selectionArgs); } try { if (mCursor != null && shouldAddToPlaylist) { mPlaylist.clear(); mPlaylist.add(new MusicTrack(mCursor.getLong(IDCOLIDX), -1)); notifyChange(QUEUE_CHANGED); mPlayPos = 0; mHistory.clear(); } } catch (final UnsupportedOperationException ex) { // Ignore } } mFileToPlay = path; mPlayer.setDataSource(mFileToPlay); if (mPlayer.isInitialized()) { mOpenFailedCounter = 0; return true; } String trackName = getTrackName(); if (TextUtils.isEmpty(trackName)) { trackName = path; } sendErrorMessage(trackName); stop(true); return false; } } private void updateCursorForDownloadedFile(Context context, Uri uri) { synchronized (this) { closeCursor(); MatrixCursor cursor = new MatrixCursor(PROJECTION_MATRIX); String title = getValueForDownloadedFile(this, uri, "title"); cursor.addRow(new Object[] { null, null, null, title, null, null, null, null }); mCursor = cursor; mCursor.moveToFirst(); } } private String getValueForDownloadedFile(Context context, Uri uri, String column) { Cursor cursor = null; final String[] projection = { column }; try { cursor = context.getContentResolver().query(uri, projection, null, null, null); if (cursor != null && cursor.moveToFirst()) { return cursor.getString(0); } } finally { if (cursor != null) { cursor.close(); } } return null; } public int getAudioSessionId() { synchronized (this) { return mPlayer.getAudioSessionId(); } } public int getMediaMountedCount() { return mMediaMountedCount; } public int getShuffleMode() { return mShuffleMode; } public void setShuffleMode(final int shufflemode) { synchronized (this) { if (mShuffleMode == shufflemode && mPlaylist.size() > 0) { return; } mShuffleMode = shufflemode; if (mShuffleMode == SHUFFLE_AUTO) { if (makeAutoShuffleList()) { mPlaylist.clear(); doAutoShuffleUpdate(); mPlayPos = 0; openCurrentAndNext(); play(); notifyChange(META_CHANGED); return; } else { mShuffleMode = SHUFFLE_NONE; } } else { setNextTrack(); } saveQueue(false); notifyChange(SHUFFLEMODE_CHANGED); } } public int getRepeatMode() { return mRepeatMode; } public void setRepeatMode(final int repeatmode) { synchronized (this) { mRepeatMode = repeatmode; setNextTrack(); saveQueue(false); notifyChange(REPEATMODE_CHANGED); } } public int removeTrack(final long id) { int numremoved = 0; synchronized (this) { for (int i = 0; i < mPlaylist.size(); i++) { if (mPlaylist.get(i).mId == id) { numremoved += removeTracksInternal(i, i); i--; } } mPlaylistInfo.remove(id); } if (numremoved > 0) { notifyChange(QUEUE_CHANGED); } return numremoved; } public boolean removeTrackAtPosition(final long id, final int position) { synchronized (this) { if (position >= 0 && position < mPlaylist.size() && mPlaylist.get(position).mId == id) { mPlaylistInfo.remove(id); return removeTracks(position, position) > 0; } } return false; } public int removeTracks(final int first, final int last) { final int numremoved = removeTracksInternal(first, last); if (numremoved > 0) { notifyChange(QUEUE_CHANGED); } return numremoved; } public int getQueuePosition() { synchronized (this) { return mPlayPos; } } public void setQueuePosition(final int index) { synchronized (this) { stop(false); mPlayPos = index; openCurrentAndNext(); play(); notifyChange(META_CHANGED); if (mShuffleMode == SHUFFLE_AUTO) { doAutoShuffleUpdate(); } } } public int getQueueHistorySize() { synchronized (this) { return mHistory.size(); } } public int getQueueHistoryPosition(int position) { synchronized (this) { if (position >= 0 && position < mHistory.size()) { return mHistory.get(position); } } return -1; } public int[] getQueueHistoryList() { synchronized (this) { int[] history = new int[mHistory.size()]; for (int i = 0; i < mHistory.size(); i++) { history[i] = mHistory.get(i); } return history; } } public String getPath() { synchronized (this) { if (mCursor == null) { return null; } return mCursor.getString(mCursor.getColumnIndexOrThrow(AudioColumns.DATA)); } } public String getAlbumName() { synchronized (this) { if (mCursor == null) { return null; } return mCursor.getString(mCursor.getColumnIndexOrThrow(AudioColumns.ALBUM)); } } public String getAlbumPath() { synchronized (this) { if (mCursor == null) { return null; } return mCursor.getString(mCursor.getColumnIndexOrThrow(AudioColumns.MIME_TYPE)); } } public String[] getAlbumPathAll() { synchronized (this) { try { int len = mPlaylistInfo.size(); String[] albums = new String[len]; long[] queue = getQueue(); for (int i = 0; i < len; i++) { albums[i] = mPlaylistInfo.get(queue[i]).albumData; } return albums; } catch (Exception e) { e.printStackTrace(); } return new String[] {}; } } public String getTrackName() { synchronized (this) { if (mCursor == null) { return null; } return mCursor.getString(mCursor.getColumnIndexOrThrow(AudioColumns.TITLE)); } } public boolean isTrackLocal() { synchronized (this) { MusicInfo info = mPlaylistInfo.get(getAudioId()); if (info == null) { return true; } return info.islocal; } } public String getAlbumPath(long id) { synchronized (this) { try { String str = mPlaylistInfo.get(id).albumData; return str; } catch (Exception e) { e.printStackTrace(); } return null; } } public String getGenreName() { synchronized (this) { if (mCursor == null || mPlayPos < 0 || mPlayPos >= mPlaylist.size()) { return null; } String[] genreProjection = { MediaStore.Audio.Genres.NAME }; Uri genreUri = MediaStore.Audio.Genres.getContentUriForAudioId("external", (int) mPlaylist.get(mPlayPos).mId); Cursor genreCursor = getContentResolver().query(genreUri, genreProjection, null, null, null); if (genreCursor != null) { try { if (genreCursor.moveToFirst()) { return genreCursor .getString(genreCursor.getColumnIndexOrThrow(MediaStore.Audio.Genres.NAME)); } } finally { genreCursor.close(); } } return null; } } public String getArtistName() { synchronized (this) { if (mCursor == null) { return null; } return mCursor.getString(mCursor.getColumnIndexOrThrow(AudioColumns.ARTIST)); } } public String getAlbumArtistName() { synchronized (this) { if (mAlbumCursor == null) { return null; } return mAlbumCursor.getString(mAlbumCursor.getColumnIndexOrThrow(AlbumColumns.ARTIST)); } } public long getAlbumId() { synchronized (this) { if (mCursor == null) { return -1; } return mCursor.getLong(mCursor.getColumnIndexOrThrow(AudioColumns.ALBUM_ID)); } } public long getArtistId() { synchronized (this) { if (mCursor == null) { return -1; } return mCursor.getLong(mCursor.getColumnIndexOrThrow(AudioColumns.ARTIST_ID)); } } public long getAudioId() { MusicTrack track = getCurrentTrack(); if (track != null) { return track.mId; } return -1; } public MusicTrack getCurrentTrack() { return getTrack(mPlayPos); } public synchronized MusicTrack getTrack(int index) { if (index >= 0 && index < mPlaylist.size()) { return mPlaylist.get(index); } return null; } public long getNextAudioId() { synchronized (this) { if (mNextPlayPos >= 0 && mNextPlayPos < mPlaylist.size() && mPlayer.isInitialized()) { return mPlaylist.get(mNextPlayPos).mId; } } return -1; } public long getPreviousAudioId() { synchronized (this) { if (mPlayer.isInitialized()) { int pos = getPreviousPlayPosition(false); if (pos >= 0 && pos < mPlaylist.size()) { return mPlaylist.get(pos).mId; } } } return -1; } public long seek(long position) { if (mPlayer.isInitialized()) { if (position < 0) { position = 0; } else if (position > mPlayer.duration()) { position = mPlayer.duration(); } long result = mPlayer.seek(position); notifyChange(POSITION_CHANGED); return result; } return -1; } public void seekRelative(long deltaInMs) { synchronized (this) { if (mPlayer.isInitialized()) { final long newPos = position() + deltaInMs; final long duration = duration(); if (newPos < 0) { prev(true); // seek to the new duration + the leftover position seek(duration() + newPos); } else if (newPos >= duration) { gotoNext(true); // seek to the leftover duration seek(newPos - duration); } else { seek(newPos); } } } } public long position() { if (mPlayer.isInitialized() && mPlayer.isTrackPrepared()) { try { return mPlayer.position(); } catch (Exception e) { e.printStackTrace(); } } return -1; } public int getSecondPosition() { if (mPlayer.isInitialized()) { return mPlayer.sencondaryPosition; } return -1; } public long duration() { if (mPlayer.isInitialized() && mPlayer.isTrackPrepared()) { return mPlayer.duration(); } return -1; } public HashMap<Long, MusicInfo> getPlayinfos() { synchronized (this) { return mPlaylistInfo; } } public long[] getQueue() { synchronized (this) { final int len = mPlaylist.size(); final long[] list = new long[len]; for (int i = 0; i < len; i++) { list[i] = mPlaylist.get(i).mId; } return list; } } public long getQueueItemAtPosition(int position) { synchronized (this) { if (position >= 0 && position < mPlaylist.size()) { return mPlaylist.get(position).mId; } } return -1; } public int getQueueSize() { synchronized (this) { return mPlaylist.size(); } } public boolean isPlaying() { return mIsSupposedToBePlaying; } private void setIsSupposedToBePlaying(boolean value, boolean notify) { if (mIsSupposedToBePlaying != value) { mIsSupposedToBePlaying = value; if (!mIsSupposedToBePlaying) { scheduleDelayedShutdown(); mLastPlayedTime = System.currentTimeMillis(); } if (notify) { notifyChange(PLAYSTATE_CHANGED); } } } private boolean recentlyPlayed() { return isPlaying() || System.currentTimeMillis() - mLastPlayedTime < IDLE_DELAY; } public void open(final HashMap<Long, MusicInfo> infos, final long[] list, final int position) { synchronized (this) { mPlaylistInfo = infos; L.D(D, TAG, mPlaylistInfo.toString()); if (mShuffleMode == SHUFFLE_AUTO) { mShuffleMode = SHUFFLE_NORMAL; } final long oldId = getAudioId(); final int listlength = list.length; boolean newlist = true; if (mPlaylist.size() == listlength) { newlist = false; for (int i = 0; i < listlength; i++) { if (list[i] != mPlaylist.get(i).mId) { newlist = true; break; } } } if (newlist) { addToPlayList(list, -1); notifyChange(QUEUE_CHANGED); } if (position >= 0) { mPlayPos = position; } else { mPlayPos = mShuffler.nextInt(mPlaylist.size()); } mHistory.clear(); openCurrentAndNextPlay(true); if (oldId != getAudioId()) { notifyChange(META_CHANGED); } } } public void stop() { stop(true); } public void play() { play(true); } public void play(boolean createNewNextTrack) { int status = mAudioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); if (D) Log.d(TAG, "Starting playback: audio focus request status = " + status); if (status != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { return; } final Intent intent = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION); intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, getAudioSessionId()); intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName()); sendBroadcast(intent); mAudioManager.registerMediaButtonEventReceiver( new ComponentName(getPackageName(), MediaButtonIntentReceiver.class.getName())); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) mSession.setActive(true); if (createNewNextTrack) { setNextTrack(); } else { setNextTrack(mNextPlayPos); } if (mPlayer.isTrackPrepared()) { final long duration = mPlayer.duration(); if (mRepeatMode != REPEAT_CURRENT && duration > 2000 && mPlayer.position() >= duration - 2000) { gotoNext(true); } } mPlayer.start(); mPlayerHandler.removeMessages(FADEDOWN); mPlayerHandler.sendEmptyMessage(FADEUP); setIsSupposedToBePlaying(true, true); cancelShutdown(); updateNotification(); notifyChange(META_CHANGED); } public void pause() { if (D) Log.d(TAG, "Pausing playback"); synchronized (this) { mPlayerHandler.removeMessages(FADEUP); if (mIsSupposedToBePlaying) { final Intent intent = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION); intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, getAudioSessionId()); intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName()); sendBroadcast(intent); mPlayer.pause(); setIsSupposedToBePlaying(false, true); notifyChange(META_CHANGED); } } } public void gotoNext(final boolean force) { if (D) Log.d(TAG, "Going to next track"); synchronized (this) { if (mPlaylist.size() <= 0) { if (D) Log.d(TAG, "No play queue"); scheduleDelayedShutdown(); return; } int pos = mNextPlayPos; if (pos < 0) { pos = getNextPosition(force); } if (pos < 0) { setIsSupposedToBePlaying(false, true); return; } stop(false); setAndRecordPlayPos(pos); openCurrentAndNext(); play(); notifyChange(META_CHANGED); notifyChange(MUSIC_CHANGED); } } public void setAndRecordPlayPos(int nextPos) { synchronized (this) { if (mShuffleMode != SHUFFLE_NONE) { mHistory.add(mPlayPos); if (mHistory.size() > MAX_HISTORY_SIZE) { mHistory.remove(0); } } mPlayPos = nextPos; } } public void prev(boolean forcePrevious) { synchronized (this) { boolean goPrevious = getRepeatMode() != REPEAT_CURRENT && (position() < REWIND_INSTEAD_PREVIOUS_THRESHOLD || forcePrevious); if (goPrevious) { if (D) Log.d(TAG, "Going to previous track"); int pos = getPreviousPlayPosition(true); if (pos < 0) { return; } mNextPlayPos = mPlayPos; mPlayPos = pos; stop(false); openCurrent(); play(false); notifyChange(META_CHANGED); notifyChange(MUSIC_CHANGED); } else { if (D) Log.d(TAG, "Going to beginning of track"); seek(0); play(false); } } } public int getPreviousPlayPosition(boolean removeFromHistory) { synchronized (this) { if (mShuffleMode == SHUFFLE_NORMAL) { final int histsize = mHistory.size(); if (histsize == 0) { return -1; } final Integer pos = mHistory.get(histsize - 1); if (removeFromHistory) { mHistory.remove(histsize - 1); } return pos.intValue(); } else { if (mPlayPos > 0) { return mPlayPos - 1; } else { return mPlaylist.size() - 1; } } } } private void openCurrent() { openCurrentAndMaybeNext(false, false); } public void moveQueueItem(int index1, int index2) { synchronized (this) { if (index1 >= mPlaylist.size()) { index1 = mPlaylist.size() - 1; } if (index2 >= mPlaylist.size()) { index2 = mPlaylist.size() - 1; } if (index1 == index2) { return; } mPlaylistInfo.remove(mPlaylist.get(index1).mId); final MusicTrack track = mPlaylist.remove(index1); if (index1 < index2) { mPlaylist.add(index2, track); if (mPlayPos == index1) { mPlayPos = index2; } else if (mPlayPos >= index1 && mPlayPos <= index2) { mPlayPos--; } } else if (index2 < index1) { mPlaylist.add(index2, track); if (mPlayPos == index1) { mPlayPos = index2; } else if (mPlayPos >= index2 && mPlayPos <= index1) { mPlayPos++; } } notifyChange(QUEUE_CHANGED); } } public void enqueue(final long[] list, final HashMap<Long, MusicInfo> map, final int action) { synchronized (this) { mPlaylistInfo.putAll(map); if (action == NEXT && mPlayPos + 1 < mPlaylist.size()) { addToPlayList(list, mPlayPos + 1); mNextPlayPos = mPlayPos + 1; notifyChange(QUEUE_CHANGED); } else { addToPlayList(list, Integer.MAX_VALUE); notifyChange(QUEUE_CHANGED); } if (mPlayPos < 0) { mPlayPos = 0; openCurrentAndNext(); play(); notifyChange(META_CHANGED); } } } private void cycleRepeat() { if (mRepeatMode == REPEAT_NONE) { setRepeatMode(REPEAT_CURRENT); if (mShuffleMode != SHUFFLE_NONE) { setShuffleMode(SHUFFLE_NONE); } } else { setRepeatMode(REPEAT_NONE); } } private void cycleShuffle() { if (mShuffleMode == SHUFFLE_NONE) { setShuffleMode(SHUFFLE_NORMAL); if (mRepeatMode == REPEAT_CURRENT) { setRepeatMode(REPEAT_ALL); } } else if (mShuffleMode == SHUFFLE_NORMAL || mShuffleMode == SHUFFLE_AUTO) { setShuffleMode(SHUFFLE_NONE); } } public void refresh() { notifyChange(REFRESH); } public void playlistChanged() { notifyChange(PLAYLIST_CHANGED); } public void loading(boolean l) { Intent intent = new Intent(MUSIC_LODING); intent.putExtra("isloading", l); sendBroadcast(intent); } public void setLockscreenAlbumArt(boolean enabled) { mShowAlbumArtOnLockscreen = enabled; notifyChange(META_CHANGED); } public void timing(int time) { PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(PAUSE_ACTION), PendingIntent.FLAG_UPDATE_CURRENT); AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE); am.set(AlarmManager.RTC, System.currentTimeMillis() + time, pendingIntent); } public interface TrackErrorExtra { String TRACK_NAME = "trackname"; } private static final class MusicPlayerHandler extends Handler { private final WeakReference<MediaService> mService; private float mCurrentVolume = 1.0f; public MusicPlayerHandler(final MediaService service, final Looper looper) { super(looper); mService = new WeakReference<MediaService>(service); } @Override public void handleMessage(final Message msg) { final MediaService service = mService.get(); if (service == null) { return; } synchronized (service) { switch (msg.what) { case FADEDOWN: mCurrentVolume -= .05f; if (mCurrentVolume > .2f) { sendEmptyMessageDelayed(FADEDOWN, 10); } else { mCurrentVolume = .2f; } service.mPlayer.setVolume(mCurrentVolume); break; case FADEUP: mCurrentVolume += .01f; if (mCurrentVolume < 1.0f) { sendEmptyMessageDelayed(FADEUP, 10); } else { mCurrentVolume = 1.0f; } service.mPlayer.setVolume(mCurrentVolume); break; case SERVER_DIED: if (service.isPlaying()) { final TrackErrorInfo info = (TrackErrorInfo) msg.obj; service.sendErrorMessage(info.mTrackName); service.removeTrack(info.mId); } else { service.openCurrentAndNext(); } break; case TRACK_WENT_TO_NEXT: service.setAndRecordPlayPos(service.mNextPlayPos); service.setNextTrack(); if (service.mCursor != null) { service.mCursor.close(); service.mCursor = null; } service.updateCursor(service.mPlaylist.get(service.mPlayPos).mId); service.notifyChange(META_CHANGED); service.notifyChange(MUSIC_CHANGED); service.updateNotification(); break; case TRACK_ENDED: if (service.mRepeatMode == REPEAT_CURRENT) { service.seek(0); service.play(); } else { if (D) Log.d(TAG, "Going to of track"); service.gotoNext(false); } break; case RELEASE_WAKELOCK: service.mWakeLock.release(); break; case FOCUSCHANGE: if (D) Log.d(TAG, "Received audio focus change event " + msg.arg1); switch (msg.arg1) { case AudioManager.AUDIOFOCUS_LOSS: case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: if (service.isPlaying()) { service.mPausedByTransientLossOfFocus = msg.arg1 == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT; } service.pause(); break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: removeMessages(FADEUP); sendEmptyMessage(FADEDOWN); break; case AudioManager.AUDIOFOCUS_GAIN: if (!service.isPlaying() && service.mPausedByTransientLossOfFocus) { service.mPausedByTransientLossOfFocus = false; mCurrentVolume = 0f; service.mPlayer.setVolume(mCurrentVolume); service.play(); } else { removeMessages(FADEDOWN); sendEmptyMessage(FADEUP); } break; default: } break; case LRC_DOWNLOADED: service.notifyChange(LRC_UPDATED); default: break; } } } } private static final class Shuffler { private final LinkedList<Integer> mHistoryOfNumbers = new LinkedList<Integer>(); private final TreeSet<Integer> mPreviousNumbers = new TreeSet<Integer>(); private final Random mRandom = new Random(); private int mPrevious; public Shuffler() { super(); } public int nextInt(final int interval) { int next; do { next = mRandom.nextInt(interval); } while (next == mPrevious && interval > 1 && !mPreviousNumbers.contains(Integer.valueOf(next))); mPrevious = next; mHistoryOfNumbers.add(mPrevious); mPreviousNumbers.add(mPrevious); cleanUpHistory(); return next; } private void cleanUpHistory() { if (!mHistoryOfNumbers.isEmpty() && mHistoryOfNumbers.size() >= MAX_HISTORY_SIZE) { for (int i = 0; i < Math.max(1, MAX_HISTORY_SIZE / 2); i++) { mPreviousNumbers.remove(mHistoryOfNumbers.removeFirst()); } } } } private static final class TrackErrorInfo { public long mId; public String mTrackName; public TrackErrorInfo(long id, String trackName) { mId = id; mTrackName = trackName; } } private static final class MultiPlayer implements MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener { private final WeakReference<MediaService> mService; private MediaPlayer mCurrentMediaPlayer = new MediaPlayer(); private MediaPlayer mNextMediaPlayer; private Handler mHandler; private boolean mIsInitialized = false; private String mNextMediaPath; private boolean isFirstLoad = true; private int sencondaryPosition = 0; private Handler handler = new Handler(); public MultiPlayer(final MediaService service) { mService = new WeakReference<MediaService>(service); mCurrentMediaPlayer.setWakeMode(mService.get(), PowerManager.PARTIAL_WAKE_LOCK); } public void setDataSource(final String path) { mIsInitialized = setDataSourceImpl(mCurrentMediaPlayer, path); if (mIsInitialized) { setNextDataSource(null); } } public void setNextDataSource(final String path) { mNextMediaPath = null; mIsNextInitialized = false; try { mCurrentMediaPlayer.setNextMediaPlayer(null); } catch (IllegalArgumentException e) { Log.i(TAG, "Next media player is current one, continuing"); } catch (IllegalStateException e) { Log.e(TAG, "Media player not initialized!"); return; } if (mNextMediaPlayer != null) { mNextMediaPlayer.release(); mNextMediaPlayer = null; } if (path == null) { return; } mNextMediaPlayer = new MediaPlayer(); mNextMediaPlayer.setWakeMode(mService.get(), PowerManager.PARTIAL_WAKE_LOCK); mNextMediaPlayer.setAudioSessionId(getAudioSessionId()); if (setNextDataSourceImpl(mNextMediaPlayer, path)) { mNextMediaPath = path; mCurrentMediaPlayer.setNextMediaPlayer(mNextMediaPlayer); // mHandler.post(setNextMediaPlayerIfPrepared); } else { if (mNextMediaPlayer != null) { mNextMediaPlayer.release(); mNextMediaPlayer = null; } } } boolean mIsTrackPrepared = false; boolean mIsTrackNet = false; boolean mIsNextTrackPrepared = false; boolean mIsNextInitialized = false; boolean mIllegalState = false; private boolean setDataSourceImpl(final MediaPlayer player, final String path) { mIsTrackNet = false; mIsTrackPrepared = false; try { player.reset(); player.setAudioStreamType(AudioManager.STREAM_MUSIC); if (path.startsWith("content://")) { player.setOnPreparedListener(null); player.setDataSource(MainApplication.context, Uri.parse(path)); player.prepare(); mIsTrackPrepared = true; player.setOnCompletionListener(this); } else { player.setDataSource(path); player.setOnPreparedListener(preparedListener); player.prepareAsync(); mIsTrackNet = true; } if (mIllegalState) { mIllegalState = false; } } catch (final IOException todo) { return false; } catch (final IllegalArgumentException todo) { return false; } catch (final IllegalStateException todo) { todo.printStackTrace(); if (!mIllegalState) { L.E(D, TAG, "mcurrentmediaplayer invoke IllegalState"); mCurrentMediaPlayer = null; mCurrentMediaPlayer = new MediaPlayer(); mCurrentMediaPlayer.setWakeMode(mService.get(), PowerManager.PARTIAL_WAKE_LOCK); mCurrentMediaPlayer.setAudioSessionId(getAudioSessionId()); setDataSourceImpl(mCurrentMediaPlayer, path); mIllegalState = true; } else { L.E(D, TAG, "mcurrentmediaplayer invoke IllegalState ,and try set again failed ,setnext"); mIllegalState = false; return false; } } player.setOnErrorListener(this); player.setOnBufferingUpdateListener(bufferingUpdateListener); return true; } private boolean setNextDataSourceImpl(final MediaPlayer player, final String path) { mIsNextTrackPrepared = false; try { player.reset(); player.setAudioStreamType(AudioManager.STREAM_MUSIC); if (path.startsWith("content://")) { player.setOnPreparedListener(preparedNextListener); player.setDataSource(MainApplication.context, Uri.parse(path)); player.prepare(); } else { player.setDataSource(path); player.setOnPreparedListener(preparedNextListener); player.prepare(); mIsNextTrackPrepared = false; } } catch (final IOException todo) { return false; } catch (final IllegalArgumentException todo) { return false; } player.setOnCompletionListener(this); player.setOnErrorListener(this); // player.setOnBufferingUpdateListener(this); return true; } public void setHandler(final Handler handler) { mHandler = handler; } public boolean isInitialized() { return mIsInitialized; } public boolean isTrackPrepared() { return mIsTrackPrepared; } public void start() { if (D) Log.d(TAG, "mIsTrackNet, " + mIsTrackNet); if (!mIsTrackNet) { mService.get().sendUpdateBuffer(100); sencondaryPosition = 100; mCurrentMediaPlayer.start(); } else { sencondaryPosition = 0; mService.get().loading(true); handler.postDelayed(startMediaPlayerIfPrepared, 50); } mService.get().notifyChange(MUSIC_CHANGED); } MediaPlayer.OnPreparedListener preparedListener = new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { if (isFirstLoad) { long seekpos = mService.get().mLastSeekPos; Log.e(TAG, "seekpos = " + seekpos); seek(seekpos >= 0 ? seekpos : 0); isFirstLoad = false; } // mService.get().notifyChange(TRACK_PREPARED); mService.get().notifyChange(META_CHANGED); mp.setOnCompletionListener(MultiPlayer.this); mIsTrackPrepared = true; } }; MediaPlayer.OnPreparedListener preparedNextListener = new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { mIsNextTrackPrepared = true; } }; MediaPlayer.OnBufferingUpdateListener bufferingUpdateListener = new MediaPlayer.OnBufferingUpdateListener() { @Override public void onBufferingUpdate(MediaPlayer mp, int percent) { if (sencondaryPosition != 100) mService.get().sendUpdateBuffer(percent); sencondaryPosition = percent; } }; Runnable setNextMediaPlayerIfPrepared = new Runnable() { int count = 0; @Override public void run() { if (mIsNextTrackPrepared && mIsInitialized) { // mCurrentMediaPlayer.setNextMediaPlayer(mNextMediaPlayer); } else if (count < 60) { handler.postDelayed(setNextMediaPlayerIfPrepared, 100); } count++; } }; Runnable startMediaPlayerIfPrepared = new Runnable() { @Override public void run() { if (D) Log.d(TAG, "mIsTrackPrepared, " + mIsTrackPrepared); if (mIsTrackPrepared) { mCurrentMediaPlayer.start(); final long duration = duration(); if (mService.get().mRepeatMode != REPEAT_CURRENT && duration > 2000 && position() >= duration - 2000) { mService.get().gotoNext(true); Log.e("play to go", ""); } mService.get().loading(false); } else { handler.postDelayed(startMediaPlayerIfPrepared, 700); } } }; public void stop() { handler.removeCallbacks(setNextMediaPlayerIfPrepared); handler.removeCallbacks(startMediaPlayerIfPrepared); mCurrentMediaPlayer.reset(); mIsInitialized = false; mIsTrackPrepared = false; } public void release() { mCurrentMediaPlayer.release(); } public void pause() { handler.removeCallbacks(startMediaPlayerIfPrepared); mCurrentMediaPlayer.pause(); } public long duration() { if (mIsTrackPrepared) { return mCurrentMediaPlayer.getDuration(); } return -1; } public long position() { if (mIsTrackPrepared) { try { return mCurrentMediaPlayer.getCurrentPosition(); } catch (Exception e) { e.printStackTrace(); } } return -1; } public long secondPosition() { if (mIsTrackPrepared) { return sencondaryPosition; } return -1; } public long seek(final long whereto) { mCurrentMediaPlayer.seekTo((int) whereto); return whereto; } public void setVolume(final float vol) { try { mCurrentMediaPlayer.setVolume(vol, vol); } catch (Exception e) { e.printStackTrace(); } } public int getAudioSessionId() { return mCurrentMediaPlayer.getAudioSessionId(); } public void setAudioSessionId(final int sessionId) { mCurrentMediaPlayer.setAudioSessionId(sessionId); } @Override public boolean onError(final MediaPlayer mp, final int what, final int extra) { Log.w(TAG, "Music Server Error what: " + what + " extra: " + extra); switch (what) { case MediaPlayer.MEDIA_ERROR_SERVER_DIED: final MediaService service = mService.get(); final TrackErrorInfo errorInfo = new TrackErrorInfo(service.getAudioId(), service.getTrackName()); mIsInitialized = false; mIsTrackPrepared = false; mCurrentMediaPlayer.release(); mCurrentMediaPlayer = new MediaPlayer(); mCurrentMediaPlayer.setWakeMode(service, PowerManager.PARTIAL_WAKE_LOCK); Message msg = mHandler.obtainMessage(SERVER_DIED, errorInfo); mHandler.sendMessageDelayed(msg, 2000); return true; default: break; } return false; } @Override public void onCompletion(final MediaPlayer mp) { Log.w(TAG, "completion"); if (mp == mCurrentMediaPlayer && mNextMediaPlayer != null) { mCurrentMediaPlayer.release(); mCurrentMediaPlayer = mNextMediaPlayer; mNextMediaPath = null; mNextMediaPlayer = null; mHandler.sendEmptyMessage(TRACK_WENT_TO_NEXT); } else { mService.get().mWakeLock.acquire(30000); mHandler.sendEmptyMessage(TRACK_ENDED); mHandler.sendEmptyMessage(RELEASE_WAKELOCK); } } } private static final class ServiceStub extends MediaAidlInterface.Stub { private final WeakReference<MediaService> mService; public boolean onTransact(int code, Parcel data, Parcel reply, int flags) { try { super.onTransact(code, data, reply, flags); } catch (final RuntimeException e) { L.E(D, TAG, "onTransact error"); e.printStackTrace(); File file = new File(mService.get().getCacheDir().getAbsolutePath() + "/err/"); if (!file.exists()) { file.mkdirs(); } try { PrintWriter writer = new PrintWriter(mService.get().getCacheDir().getAbsolutePath() + "/err/" + System.currentTimeMillis() + "_aidl.log"); e.printStackTrace(writer); writer.close(); } catch (Exception e1) { e1.printStackTrace(); } new Thread(new Runnable() { @Override public void run() { CommonUtils.sendTextMail("err aidl log from " + CommonUtils.getUniquePsuedoID(), CommonUtils.getDeviceInfo() + Log.getStackTraceString(e)); } }).start(); throw e; } catch (RemoteException e) { e.printStackTrace(); } return true; } private ServiceStub(final MediaService service) { mService = new WeakReference<MediaService>(service); } @Override public void openFile(final String path) throws RemoteException { mService.get().openFile(path); } @Override public void open(final Map infos, final long[] list, final int position) throws RemoteException { mService.get().open((HashMap<Long, MusicInfo>) infos, list, position); } @Override public void stop() throws RemoteException { mService.get().stop(); } @Override public void pause() throws RemoteException { mService.get().pause(); } @Override public void play() throws RemoteException { mService.get().play(); } @Override public void prev(boolean forcePrevious) throws RemoteException { mService.get().prev(forcePrevious); } @Override public void next() throws RemoteException { mService.get().gotoNext(true); } @Override public void enqueue(final long[] list, final Map infos, final int action) throws RemoteException { mService.get().enqueue(list, (HashMap<Long, MusicInfo>) infos, action); } @Override public Map getPlayinfos() throws RemoteException { return mService.get().getPlayinfos(); } @Override public void moveQueueItem(final int from, final int to) throws RemoteException { mService.get().moveQueueItem(from, to); } @Override public void refresh() throws RemoteException { mService.get().refresh(); } @Override public void playlistChanged() throws RemoteException { mService.get().playlistChanged(); } @Override public boolean isPlaying() throws RemoteException { return mService.get().isPlaying(); } @Override public long[] getQueue() throws RemoteException { return mService.get().getQueue(); } @Override public long getQueueItemAtPosition(int position) throws RemoteException { return mService.get().getQueueItemAtPosition(position); } @Override public int getQueueSize() throws RemoteException { return mService.get().getQueueSize(); } @Override public int getQueueHistoryPosition(int position) throws RemoteException { return mService.get().getQueueHistoryPosition(position); } @Override public int getQueueHistorySize() throws RemoteException { return mService.get().getQueueHistorySize(); } @Override public int[] getQueueHistoryList() throws RemoteException { return mService.get().getQueueHistoryList(); } @Override public long duration() throws RemoteException { return mService.get().duration(); } @Override public long position() throws RemoteException { return mService.get().position(); } @Override public int secondPosition() throws RemoteException { return mService.get().getSecondPosition(); } @Override public long seek(final long position) throws RemoteException { return mService.get().seek(position); } @Override public void seekRelative(final long deltaInMs) throws RemoteException { mService.get().seekRelative(deltaInMs); } @Override public long getAudioId() throws RemoteException { return mService.get().getAudioId(); } @Override public MusicTrack getCurrentTrack() throws RemoteException { return mService.get().getCurrentTrack(); } @Override public MusicTrack getTrack(int index) throws RemoteException { return mService.get().getTrack(index); } @Override public long getNextAudioId() throws RemoteException { return mService.get().getNextAudioId(); } @Override public long getPreviousAudioId() throws RemoteException { return mService.get().getPreviousAudioId(); } @Override public long getArtistId() throws RemoteException { return mService.get().getArtistId(); } @Override public long getAlbumId() throws RemoteException { return mService.get().getAlbumId(); } @Override public String getArtistName() throws RemoteException { return mService.get().getArtistName(); } @Override public String getTrackName() throws RemoteException { return mService.get().getTrackName(); } @Override public boolean isTrackLocal() throws RemoteException { return mService.get().isTrackLocal(); } @Override public String getAlbumName() throws RemoteException { return mService.get().getAlbumName(); } @Override public String getAlbumPath() throws RemoteException { return mService.get().getAlbumPath(); } @Override public String[] getAlbumPathtAll() throws RemoteException { return mService.get().getAlbumPathAll(); } @Override public String getPath() throws RemoteException { return mService.get().getPath(); } @Override public int getQueuePosition() throws RemoteException { return mService.get().getQueuePosition(); } @Override public void setQueuePosition(final int index) throws RemoteException { mService.get().setQueuePosition(index); } @Override public int getShuffleMode() throws RemoteException { return mService.get().getShuffleMode(); } @Override public void setShuffleMode(final int shufflemode) throws RemoteException { mService.get().setShuffleMode(shufflemode); } @Override public int getRepeatMode() throws RemoteException { return mService.get().getRepeatMode(); } @Override public void setRepeatMode(final int repeatmode) throws RemoteException { mService.get().setRepeatMode(repeatmode); } @Override public int removeTracks(final int first, final int last) throws RemoteException { return mService.get().removeTracks(first, last); } @Override public int removeTrack(final long id) throws RemoteException { return mService.get().removeTrack(id); } @Override public boolean removeTrackAtPosition(final long id, final int position) throws RemoteException { return mService.get().removeTrackAtPosition(id, position); } @Override public int getMediaMountedCount() throws RemoteException { return mService.get().getMediaMountedCount(); } @Override public int getAudioSessionId() throws RemoteException { return mService.get().getAudioSessionId(); } @Override public void setLockscreenAlbumArt(boolean enabled) { mService.get().setLockscreenAlbumArt(enabled); } @Override public void exit() throws RemoteException { mService.get().exit(); } @Override public void timing(int time) throws RemoteException { mService.get().timing(time); } } private class MediaStoreObserver extends ContentObserver implements Runnable { private static final long REFRESH_DELAY = 500; private Handler mHandler; public MediaStoreObserver(Handler handler) { super(handler); mHandler = handler; } @Override public void onChange(boolean selfChange) { mHandler.removeCallbacks(this); mHandler.postDelayed(this, REFRESH_DELAY); } @Override public void run() { Log.e("ELEVEN", "calling refresh!"); refresh(); } } }