/*
* 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.wm.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.wm.remusic.MainApplication;
import com.wm.remusic.MediaAidlInterface;
import com.wm.remusic.R;
import com.wm.remusic.activity.LockActivity;
import com.wm.remusic.downmusic.Down;
import com.wm.remusic.info.MusicInfo;
import com.wm.remusic.json.MusicFileDownInfo;
import com.wm.remusic.net.BMA;
import com.wm.remusic.net.HttpUtil;
import com.wm.remusic.permissions.Nammu;
import com.wm.remusic.provider.MusicPlaybackState;
import com.wm.remusic.provider.RecentStore;
import com.wm.remusic.proxy.utils.MediaPlayerProxy;
import com.wm.remusic.receiver.MediaButtonIntentReceiver;
import com.wm.remusic.recent.SongPlayCount;
import com.wm.remusic.uitl.CommonUtils;
import com.wm.remusic.uitl.ImageUtils;
import com.wm.remusic.uitl.L;
import com.wm.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.wm.remusic.playstatechanged";
public static final String POSITION_CHANGED = "com.wm.remusic.positionchanged";
public static final String META_CHANGED = "com.wm.remusic.metachanged";
public static final String PLAYLIST_ITEM_MOVED = "com.wm.remusic.mmoved";
public static final String QUEUE_CHANGED = "com.wm.remusic.queuechanged";
public static final String PLAYLIST_CHANGED = "com.wm.remusic.playlistchanged";
public static final String REPEATMODE_CHANGED = "com.wm.remusic.repeatmodechanged";
public static final String SHUFFLEMODE_CHANGED = "com.wm.remusic.shufflemodechanged";
public static final String TRACK_ERROR = "com.wm.remusic.trackerror";
public static final String TIMBER_PACKAGE_NAME = "com.wm.remusic";
public static final String MUSIC_PACKAGE_NAME = "com.android.music";
public static final String SERVICECMD = "com.wm.remusic.musicservicecommand";
public static final String TOGGLEPAUSE_ACTION = "com.wm.remusic.togglepause";
public static final String PAUSE_ACTION = "com.wm.remusic.pause";
public static final String STOP_ACTION = "com.wm.remusic.stop";
public static final String PREVIOUS_ACTION = "com.wm.remusic.previous";
public static final String PREVIOUS_FORCE_ACTION = "com.wm.remusic.previous.force";
public static final String NEXT_ACTION = "com.wm.remusic.next";
public static final String MUSIC_CHANGED = "com.wm.remusi.change_music";
public static final String REPEAT_ACTION = "com.wm.remusic.repeat";
public static final String SHUFFLE_ACTION = "com.wm.remusic.shuffle";
public static final String FROM_MEDIA_BUTTON = "frommediabutton";
public static final String REFRESH = "com.wm.remusic.refresh";
public static final String LRC_UPDATED = "com.wm.remusic.updatelrc";
public static final String UPDATE_LOCKSCREEN = "com.wm.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.wm.remusic.prepared";
public static final String TRY_GET_TRACKINFO = "com.wm.remusic.gettrackinfo";
public static final String BUFFER_UP = "com.wm.remusic.bufferup";
public static final String LOCK_SCREEN = "com.wm.remusic.lock";
public static final String SEND_PROGRESS = "com.wm.remusic.progress";
public static final String MUSIC_LODING = "com.wm.remusic.loading";
private static final String SHUTDOWN = "com.wm.remusic.shutdown";
public static final String SETQUEUE = "com.wm.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.wm.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.wm.remusic.LAUNCH_NOW_PLAYING_ACTION");
nowPlayingIntent.setComponent(new ComponentName("com.wm.remusic","com.wm.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();
}
}
}