Merge tag 'android-6.0.0_r26' into HEAD
Android 6.0.0 release 26
* tag 'android-6.0.0_r26':
Import translations. DO NOT MERGE
Import translations. DO NOT MERGE
Change-Id: Ia9bff9db1e5371da310dacd01f2ff85f398be4c5
diff --git a/Android.mk b/Android.mk
index d2b7691..5102b27 100644
--- a/Android.mk
+++ b/Android.mk
@@ -8,8 +8,6 @@
LOCAL_PACKAGE_NAME := Music
-LOCAL_SDK_VERSION := current
-
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
include $(BUILD_PACKAGE)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2db6259..c2ba509 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -26,10 +26,12 @@
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
+ <uses-permission android:name="android.permission.BLUETOOTH" />
<application android:icon="@drawable/app_music"
android:label="@string/musicbrowserlabel"
android:taskAffinity="android.task.music"
+ android:hardwareAccelerated="true"
android:allowTaskReparenting="true"
android:usesCleartextTraffic="true">
<meta-data
diff --git a/src/com/android/music/MediaPlaybackActivity.java b/src/com/android/music/MediaPlaybackActivity.java
index 2f04978..9b97f71 100644
--- a/src/com/android/music/MediaPlaybackActivity.java
+++ b/src/com/android/music/MediaPlaybackActivity.java
@@ -87,6 +87,7 @@
private Toast mToast;
private int mTouchSlop;
private ServiceToken mToken;
+ private boolean mReceiverUnregistered = false;
public MediaPlaybackActivity()
{
@@ -482,6 +483,8 @@
IntentFilter f = new IntentFilter();
f.addAction(MediaPlaybackService.PLAYSTATE_CHANGED);
f.addAction(MediaPlaybackService.META_CHANGED);
+ f.addAction(MediaPlaybackService.SHUFFLE_CHANGED);
+ f.addAction(MediaPlaybackService.REPEAT_CHANGED);
registerReceiver(mStatusListener, new IntentFilter(f));
updateTrackInfo();
long next = refreshNow();
@@ -1253,6 +1256,43 @@
queueNextRefresh(1);
} else if (action.equals(MediaPlaybackService.PLAYSTATE_CHANGED)) {
setPauseButtonImage();
+ } else if (action.equals(MediaPlaybackService.SHUFFLE_CHANGED)) {
+ setShuffleButtonImage();
+ } else if (action.equals(MediaPlaybackService.REPEAT_CHANGED)) {
+ setRepeatButtonImage();
+ }
+ }
+ };
+
+ private BroadcastReceiver mScreenTimeoutListener = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
+ if (mReceiverUnregistered) {
+ IntentFilter f = new IntentFilter();
+ f.addAction(MediaPlaybackService.PLAYSTATE_CHANGED);
+ f.addAction(MediaPlaybackService.META_CHANGED);
+ f.addAction(MediaPlaybackService.SHUFFLE_CHANGED);
+ f.addAction(MediaPlaybackService.REPEAT_CHANGED);
+ registerReceiver(mStatusListener, f);
+ mReceiverUnregistered = false;
+ }
+ paused = false;
+
+ if (mPosOverride > 0) {
+ mPosOverride = -1;
+ }
+ updateTrackInfo();
+ long next = refreshNow();
+ queueNextRefresh(next);
+ } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
+ paused = false;
+
+ if (!mReceiverUnregistered) {
+ mHandler.removeMessages(REFRESH);
+ unregisterReceiver(mStatusListener);
+ mReceiverUnregistered = true;
+ }
}
}
};
diff --git a/src/com/android/music/MediaPlaybackService.java b/src/com/android/music/MediaPlaybackService.java
index 25d60c7..dbfab4c 100644
--- a/src/com/android/music/MediaPlaybackService.java
+++ b/src/com/android/music/MediaPlaybackService.java
@@ -47,6 +47,7 @@
import android.os.Message;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.PowerManager.WakeLock;
import android.provider.MediaStore;
import android.util.Log;
@@ -59,6 +60,7 @@
import java.lang.ref.WeakReference;
import java.util.Random;
import java.util.Vector;
+import java.util.HashMap;
/**
* Provides "background" audio playback capabilities, allowing the
@@ -81,9 +83,12 @@
public static final int REPEAT_NONE = 0;
public static final int REPEAT_CURRENT = 1;
public static final int REPEAT_ALL = 2;
+ private HashMap<Byte, Boolean> mAttributePairs = new HashMap<Byte, Boolean>();
public static final String PLAYSTATE_CHANGED = "com.android.music.playstatechanged";
public static final String META_CHANGED = "com.android.music.metachanged";
+ public static final String SHUFFLE_CHANGED = "org.codeaurora.music.shuffle";
+ public static final String REPEAT_CHANGED = "org.codeaurora.music.repeat";
public static final String QUEUE_CHANGED = "com.android.music.queuechanged";
public static final String SERVICECMD = "com.android.music.musicservicecommand";
@@ -94,11 +99,20 @@
public static final String CMDPLAY = "play";
public static final String CMDPREVIOUS = "previous";
public static final String CMDNEXT = "next";
+ public static final String CMDGET = "get";
+ public static final String CMDSET = "set";
public static final String TOGGLEPAUSE_ACTION = "com.android.music.musicservicecommand.togglepause";
public static final String PAUSE_ACTION = "com.android.music.musicservicecommand.pause";
public static final String PREVIOUS_ACTION = "com.android.music.musicservicecommand.previous";
public static final String NEXT_ACTION = "com.android.music.musicservicecommand.next";
+ private static final String PLAYSTATUS_REQUEST = "org.codeaurora.android.music.playstatusrequest";
+ private static final String PLAYSTATUS_RESPONSE = "org.codeaurora.music.playstatusresponse";
+ private static final String PLAYERSETTINGS_REQUEST = "org.codeaurora.music.playersettingsrequest";
+ private static final String PLAYERSETTINGS_RESPONSE = "org.codeaurora.music.playersettingsresponse";
+ private static final String SET_ADDRESSED_PLAYER = "org.codeaurora.music.setaddressedplayer";
+ private static final String EXTRA_SHUFFLE_VAL = "shuffle";
+ private static final String EXTRA_REPEAT_VAL = "repeat";
private static final int TRACK_ENDED = 1;
private static final int RELEASE_WAKELOCK = 2;
@@ -107,8 +121,18 @@
private static final int FADEDOWN = 5;
private static final int FADEUP = 6;
private static final int TRACK_WENT_TO_NEXT = 7;
+ private static final int ERROR = 8 ;
private static final int MAX_HISTORY_SIZE = 100;
-
+ private static final int DEFAULT_REPEAT_VAL = 0;
+ private static final int DEFAULT_SHUFFLE_VAL = 0;
+ private static final int SET_BROWSED_PLAYER = 1001;
+ private static final int SET_PLAY_ITEM = 1002;
+ private static final int GET_NOW_PLAYING_ENTRIES = 1003;
+
+ private static final int SCOPE_FILE_SYSTEM = 0x01;
+ private static final int SCOPE_NOW_PLAYING = 0x03;
+ private static final int INVALID_SONG_UID = 0xffffffff;
+
private MultiPlayer mPlayer;
private String mFileToPlay;
private int mShuffleMode = SHUFFLE_NONE;
@@ -140,6 +164,7 @@
private final static int PODCASTCOLIDX = 8;
private final static int BOOKMARKCOLIDX = 9;
private BroadcastReceiver mUnmountReceiver = null;
+ private BroadcastReceiver mA2dpReceiver = null;
private WakeLock mWakeLock;
private int mServiceStartId = -1;
private boolean mServiceInUse = false;
@@ -149,6 +174,71 @@
private boolean mQueueIsSaveable = true;
// used to track what type of audio focus loss caused the playback to pause
private boolean mPausedByTransientLossOfFocus = false;
+ private SetBrowsedPlayerMonitor mSetBrowsedPlayerMonitor;
+ private SetPlayItemMonitor mSetPlayItemMonitor;
+ private GetNowPlayingEntriesMonitor mGetNowPlayingEntriesMonitor;
+ public static final byte ATTRIBUTE_ALL = -1;
+ public static final byte ERROR_NOTSUPPORTED = -1;
+ public static final byte ATTRIBUTE_EQUALIZER = 1;
+ public static final byte ATTRIBUTE_REPEATMODE = 2;
+ public static final byte ATTRIBUTE_SHUFFLEMODE = 3;
+ public static final byte ATTRIBUTE_SCANMODE = 4;
+
+ private byte [] supportedAttributes = new byte[] {
+ ATTRIBUTE_REPEATMODE,
+ ATTRIBUTE_SHUFFLEMODE
+ };
+
+ public static final byte VALUE_REPEATMODE_OFF = 1;
+ public static final byte VALUE_REPEATMODE_SINGLE = 2;
+ public static final byte VALUE_REPEATMODE_ALL = 3;
+ public static final byte VALUE_REPEATMODE_GROUP = 4;
+
+ private byte [] supportedRepeatValues = new byte [] {
+ VALUE_REPEATMODE_OFF,
+ VALUE_REPEATMODE_SINGLE,
+ VALUE_REPEATMODE_ALL
+ };
+
+ public static final byte VALUE_SHUFFLEMODE_OFF = 1;
+ public static final byte VALUE_SHUFFLEMODE_ALL = 2;
+ public static final byte VALUE_SHUFFLEMODE_GROUP = 3;
+
+ private byte [] supportedShuffleValues = new byte [] {
+ VALUE_SHUFFLEMODE_OFF,
+ VALUE_SHUFFLEMODE_ALL
+ };
+
+ String [] AttrStr = new String[] {
+ "",
+ "Equalizer",
+ "Repeat Mode",
+ "Shuffle Mode",
+ "Scan Mode"
+ };
+
+ private byte [] unsupportedList = new byte [] {
+ 0
+ };
+ private static final String EXTRA_GET_COMMAND = "commandExtra";
+ private static final String EXTRA_GET_RESPONSE = "Response";
+ private static final int GET_ATTRIBUTE_IDS = 0;
+ private static final int GET_VALUE_IDS = 1;
+ private static final int GET_ATTRIBUTE_TEXT = 2;
+ private static final int GET_VALUE_TEXT = 3;
+ private static final int GET_ATTRIBUTE_VALUES = 4;
+ private static final int NOTIFY_ATTRIBUTE_VALUES = 5;
+ private static final int SET_ATTRIBUTE_VALUES = 6;
+ private static final int GET_INVALID = 0xff;
+ private static final byte GET_ATTR_INVALID = 0x7f;
+
+ private static final String EXTRA_ATTRIBUTE_ID = "Attribute";
+ private static final String EXTRA_VALUE_STRING_ARRAY = "ValueStrings";
+ private static final String EXTRA_ATTRIB_VALUE_PAIRS = "AttribValuePairs";
+ private static final String EXTRA_ATTRIBUTE_STRING_ARRAY = "AttributeStrings";
+ private static final String EXTRA_VALUE_ID_ARRAY = "Values";
+ private static final String EXTRA_ATTIBUTE_ID_ARRAY = "Attributes";
+
private SharedPreferences mPreferences;
// We use this to distinguish between different cards when saving/restoring playlists.
@@ -339,15 +429,29 @@
mCardId = MusicUtils.getCardId(this);
registerExternalStorageListener();
+ registerA2dpServiceListener();
// Needs to be done in this thread, since otherwise ApplicationContext.getPowerManager() crashes.
mPlayer = new MultiPlayer();
mPlayer.setHandler(mMediaplayerHandler);
+ mSetBrowsedPlayerMonitor = new SetBrowsedPlayerMonitor();
+ mRemoteControlClient.setBrowsedPlayerUpdateListener(mSetBrowsedPlayerMonitor);
+
+ mSetPlayItemMonitor = new SetPlayItemMonitor();
+ mRemoteControlClient.setPlayItemListener(mSetPlayItemMonitor);
+
+ mGetNowPlayingEntriesMonitor = new GetNowPlayingEntriesMonitor();
+ mRemoteControlClient.setNowPlayingEntriesUpdateListener(mGetNowPlayingEntriesMonitor);
+
reloadQueue();
notifyChange(QUEUE_CHANGED);
notifyChange(META_CHANGED);
+ mAttributePairs.put(ATTRIBUTE_EQUALIZER, false);
+ mAttributePairs.put(ATTRIBUTE_REPEATMODE, true);
+ mAttributePairs.put(ATTRIBUTE_SHUFFLEMODE, true);
+
IntentFilter commandFilter = new IntentFilter();
commandFilter.addAction(SERVICECMD);
commandFilter.addAction(TOGGLEPAUSE_ACTION);
@@ -397,6 +501,16 @@
unregisterReceiver(mUnmountReceiver);
mUnmountReceiver = null;
}
+
+ if (mA2dpReceiver != null) {
+ unregisterReceiver(mA2dpReceiver);
+ mA2dpReceiver = null;
+ }
+
+ if (mAttributePairs != null) {
+ mAttributePairs.clear();
+ }
+
mWakeLock.release();
super.onDestroy();
}
@@ -709,7 +823,71 @@
stopSelf(mServiceStartId);
return true;
}
-
+
+ private void getNowPlayingEntries() {
+ Log.i(LOGTAG, "getNowPlayingEntries: num of items: " + mPlayListLen);
+ synchronized (mPlayList) {
+ long [] nowPlayingList = new long[mPlayListLen];
+ for (int count = 0; count < mPlayListLen; count++) {
+ nowPlayingList[count] = mPlayList[count];
+ }
+ mRemoteControlClient.updateNowPlayingEntries(nowPlayingList);
+ }
+ }
+
+ private void setBrowsedPlayer() {
+ Log.i(LOGTAG, "setBrowsedPlayer");
+ Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+ Log.i(LOGTAG, "URI: " + uri);
+ mRemoteControlClient.updateFolderInfoBrowsedPlayer(uri.toString());
+ }
+
+ private void playItem(int scope, long playItemUid) {
+ boolean success = false;
+ Log.i(LOGTAG, "playItem uid: " + playItemUid + " scope: " + scope);
+ if (playItemUid < 0) {
+ mRemoteControlClient.playItemResponse(success);
+ return;
+ } else if (scope == SCOPE_FILE_SYSTEM) {
+ success = openItem(playItemUid);
+ } else if (scope == SCOPE_NOW_PLAYING) {
+ for (int index = 0; index < mPlayListLen; index++) {
+ if (mPlayList[index] == playItemUid) {
+ Log.i(LOGTAG, "Now Playing list contains UID at " + index);
+ success = true;
+ break;
+ }
+ }
+ if (success) {
+ success = openItem(playItemUid);
+ }
+ }
+ mRemoteControlClient.playItemResponse(success);
+ }
+
+ private boolean openItem (long playItemUid) {
+ boolean isSuccess = false;
+ if (mCursor != null) {
+ mCursor.close();
+ mCursor = null;
+ }
+ if (mPlayListLen == 0) {
+ Log.e(LOGTAG, "Playlist Length = 0");
+ return isSuccess;
+ }
+ stop(false);
+ mCursor = getCursorForId(playItemUid);
+ if (mCursor != null) {
+ long [] list = new long[] { playItemUid };
+ enqueue(list, NOW);
+ Log.i(LOGTAG, "Opened UID: " + playItemUid);
+ isSuccess = true;
+ } else {
+ Log.e(LOGTAG, "Cursor could not be fetched");
+ }
+ return isSuccess;
+ }
+
private Handler mDelayedStopHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -726,6 +904,24 @@
}
};
+ private Handler mAvrcpHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case SET_BROWSED_PLAYER:
+ setBrowsedPlayer();
+ break;
+ case SET_PLAY_ITEM:
+ playItem(msg.arg1, ((Long)msg.obj).longValue());
+ break;
+ case GET_NOW_PLAYING_ENTRIES:
+ getNowPlayingEntries();
+ break;
+ default:
+ }
+ }
+ };
+
/**
* Called when we receive a ACTION_MEDIA_EJECT notification.
*
@@ -771,6 +967,69 @@
}
}
+ public void registerA2dpServiceListener() {
+ mA2dpReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ String cmd = intent.getStringExtra("command");
+ if (action.equals(SET_ADDRESSED_PLAYER)) {
+ play(); // this ensures audio focus change is called and play the media
+ } else if (action.equals(PLAYSTATUS_REQUEST)) {
+ notifyChange(PLAYSTATUS_RESPONSE);
+ } else if (PLAYERSETTINGS_REQUEST.equals(action)) {
+ if (CMDGET.equals(cmd)) {
+ int getCommand = intent.getIntExtra(EXTRA_GET_COMMAND,
+ GET_INVALID);
+ byte attribute;
+ byte [] attrIds; byte [] valIds;
+ switch (getCommand) {
+ case GET_ATTRIBUTE_IDS:
+ notifyAttributeIDs(PLAYERSETTINGS_RESPONSE);
+ break;
+ case GET_VALUE_IDS:
+ attribute =
+ intent.getByteExtra(EXTRA_ATTRIBUTE_ID,
+ GET_ATTR_INVALID);
+ notifyValueIDs(PLAYERSETTINGS_RESPONSE, attribute);
+ break;
+ case GET_ATTRIBUTE_TEXT:
+ attrIds = intent.getByteArrayExtra(
+ EXTRA_ATTIBUTE_ID_ARRAY);
+ notifyAttributesText(PLAYERSETTINGS_RESPONSE, attrIds);
+ break;
+ case GET_VALUE_TEXT:
+ attribute =
+ intent.getByteExtra(EXTRA_ATTRIBUTE_ID,
+ GET_ATTR_INVALID);
+ valIds = intent.getByteArrayExtra(
+ EXTRA_VALUE_ID_ARRAY);
+ notifyAttributeValuesText(
+ PLAYERSETTINGS_RESPONSE, attribute, valIds);
+ break;
+ case GET_ATTRIBUTE_VALUES:
+ notifyAttributeValues(PLAYERSETTINGS_RESPONSE,
+ mAttributePairs, GET_ATTRIBUTE_VALUES);
+ break;
+ default:
+ Log.e(LOGTAG, "invalid getCommand"+getCommand);
+ break;
+ }
+ } else if (CMDSET.equals(cmd)){
+ byte[] attribValuePairs = intent.getByteArrayExtra(
+ EXTRA_ATTRIB_VALUE_PAIRS);
+ setValidAttributes(attribValuePairs);
+ }
+ }
+ }
+ };
+ IntentFilter iFilter = new IntentFilter();
+ iFilter.addAction(SET_ADDRESSED_PLAYER);
+ iFilter.addAction(PLAYSTATUS_REQUEST);
+ iFilter.addAction(PLAYERSETTINGS_REQUEST);
+ registerReceiver(mA2dpReceiver, iFilter);
+ }
+
/**
* Notify the change-receivers that something has changed.
* The intent that is sent contains the following data
@@ -801,19 +1060,35 @@
sendStickyBroadcast(i);
if (what.equals(PLAYSTATE_CHANGED)) {
- mRemoteControlClient.setPlaybackState(isPlaying() ?
- RemoteControlClient.PLAYSTATE_PLAYING : RemoteControlClient.PLAYSTATE_PAUSED);
+ mRemoteControlClient.setPlaybackState((isPlaying() ?
+ RemoteControlClient.PLAYSTATE_PLAYING : RemoteControlClient.PLAYSTATE_PAUSED),
+ position() , RemoteControlClient.PLAYBACK_SPEED_1X);
} else if (what.equals(META_CHANGED)) {
RemoteControlClient.MetadataEditor ed = mRemoteControlClient.editMetadata(true);
ed.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, getTrackName());
ed.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, getAlbumName());
- ed.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, getArtistName());
+ ed.putString(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, getArtistName());
ed.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, duration());
+ if ((mPlayList != null) && (mPlayPos >= 0) && (mPlayPos < mPlayList.length)) {
+ ed.putLong(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER,
+ mPlayList[mPlayPos]);
+ } else {
+ ed.putLong(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER,
+ INVALID_SONG_UID);
+ }
+ ed.putLong(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER, mPlayPos);
+ try {
+ ed.putLong(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS, mPlayListLen);
+ } catch (IllegalArgumentException e) {
+ Log.e(LOGTAG, "METADATA_KEY_NUM_TRACKS: failed: " + e);
+ }
Bitmap b = MusicUtils.getArtwork(this, getAudioId(), getAlbumId(), false);
if (b != null) {
ed.putBitmap(MetadataEditor.BITMAP_KEY_ARTWORK, b);
}
ed.apply();
+ } else if (what.equals(QUEUE_CHANGED)) {
+ mRemoteControlClient.updateNowPlayingContentChange();
}
if (what.equals(QUEUE_CHANGED)) {
@@ -1085,12 +1360,14 @@
}
private void setNextTrack() {
- mNextPlayPos = getNextPosition(false);
- if (mNextPlayPos >= 0) {
- long id = mPlayList[mNextPlayPos];
- mPlayer.setNextDataSource(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "/" + id);
- } else {
- mPlayer.setNextDataSource(null);
+ if(SystemProperties.getBoolean("gapless.audio.decode", false)) {
+ mNextPlayPos = getNextPosition(false);
+ if (mNextPlayPos >= 0) {
+ long id = mPlayList[mNextPlayPos];
+ mPlayer.setNextDataSource(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "/" + id);
+ } else {
+ mPlayer.setNextDataSource(null);
+ }
}
}
@@ -1158,6 +1435,7 @@
AudioManager.AUDIOFOCUS_GAIN);
mAudioManager.registerMediaButtonEventReceiver(new ComponentName(this.getPackageName(),
MediaButtonIntentReceiver.class.getName()));
+ mAudioManager.registerRemoteControlClient(mRemoteControlClient);
if (mPlayer.isInitialized()) {
// if we are at the end of the song, go to the next song first
@@ -1527,6 +1805,33 @@
return false;
}
+ private class SetBrowsedPlayerMonitor implements
+ RemoteControlClient.OnSetBrowsedPlayerListener{
+ @Override
+ public void onSetBrowsedPlayer() {
+ Log.d(LOGTAG, "onSetBrowsedPlayer");
+ mAvrcpHandler.obtainMessage(SET_BROWSED_PLAYER).sendToTarget();
+ }
+ };
+
+ private class SetPlayItemMonitor implements
+ RemoteControlClient.OnSetPlayItemListener{
+ @Override
+ public void onSetPlayItem(int scope, long uid) {
+ Log.d(LOGTAG, "onSetPlayItem");
+ mAvrcpHandler.obtainMessage(SET_PLAY_ITEM, scope, 0, new Long(uid)).sendToTarget();
+ }
+ };
+
+ private class GetNowPlayingEntriesMonitor implements
+ RemoteControlClient.OnGetNowPlayingEntriesListener{
+ @Override
+ public void onGetNowPlayingEntries() {
+ Log.d(LOGTAG, "onGetNowPlayingEntries");
+ mAvrcpHandler.obtainMessage(GET_NOW_PLAYING_ENTRIES).sendToTarget();
+ }
+ };
+
// A simple variation of Random that makes sure that the
// value it returns is not equal to the value it returned
// previously, unless the interval is 1.
@@ -1655,6 +1960,12 @@
public void setShuffleMode(int shufflemode) {
synchronized(this) {
if (mShuffleMode == shufflemode && mPlayListLen > 0) {
+ /**
+ * Some carkits send Shuffle Values same as our current
+ * values. In such cases we need to respond back to ck
+ */
+ notifyAttributeValues(PLAYERSETTINGS_RESPONSE,
+ mAttributePairs, SET_ATTRIBUTE_VALUES);
return;
}
mShuffleMode = shufflemode;
@@ -1666,12 +1977,17 @@
openCurrentAndNext();
play();
notifyChange(META_CHANGED);
+ notifyAttributeValues(PLAYERSETTINGS_RESPONSE,
+ mAttributePairs, SET_ATTRIBUTE_VALUES);
return;
} else {
// failed to build a list of files to shuffle
mShuffleMode = SHUFFLE_NONE;
}
}
+ notifyAttributeValues(PLAYERSETTINGS_RESPONSE,
+ mAttributePairs, SET_ATTRIBUTE_VALUES);
+ notifyChange(SHUFFLE_CHANGED);
saveQueue(false);
}
}
@@ -1683,6 +1999,9 @@
synchronized(this) {
mRepeatMode = repeatmode;
setNextTrack();
+ notifyAttributeValues(PLAYERSETTINGS_RESPONSE,
+ mAttributePairs, SET_ATTRIBUTE_VALUES);
+ notifyChange(REPEAT_CHANGED);
saveQueue(false);
}
}
@@ -1861,6 +2180,240 @@
}
/**
+ * Returns the player supported attribute IDs.
+ */
+ private void notifyAttributeIDs(String what) {
+ Intent i = new Intent(what);
+ i.putExtra(EXTRA_GET_RESPONSE, GET_ATTRIBUTE_IDS);
+ i.putExtra(EXTRA_ATTIBUTE_ID_ARRAY, supportedAttributes);
+ Log.e(LOGTAG, "notifying attributes");
+ sendBroadcast(i);
+ }
+
+ /**
+ * Returns the player supported value IDs for given attrib.
+ */
+ private void notifyValueIDs(String what, byte attribute) {
+ Intent intent = new Intent(what);
+ intent.putExtra(EXTRA_GET_RESPONSE, GET_VALUE_IDS);
+ intent.putExtra(EXTRA_ATTRIBUTE_ID, attribute);
+ switch (attribute) {
+ case ATTRIBUTE_REPEATMODE:
+ intent.putExtra(EXTRA_VALUE_ID_ARRAY, supportedRepeatValues);
+ break;
+ case ATTRIBUTE_SHUFFLEMODE:
+ intent.putExtra(EXTRA_VALUE_ID_ARRAY, supportedShuffleValues);
+ break;
+ default:
+ Log.e(LOGTAG,"unsupported attribute"+attribute);
+ intent.putExtra(EXTRA_VALUE_ID_ARRAY, unsupportedList);
+ break;
+ }
+ sendBroadcast(intent);
+ }
+
+ /**
+ * Returns the player supported attrib text for given IDs.
+ */
+ private void notifyAttributesText(String what, byte [] attrIds) {
+ String [] AttribStrings = new String [attrIds.length];
+ Intent intent = new Intent(what);
+ intent.putExtra(EXTRA_GET_RESPONSE, GET_ATTRIBUTE_TEXT);
+ for (int i = 0; i < attrIds.length; i++) {
+ if (attrIds[i] >= AttrStr.length) {
+ Log.e(LOGTAG, "attrib id is"+attrIds[i]+"which is not supported");
+ AttribStrings[i] = "";
+ } else {
+ AttribStrings[i] = AttrStr[attrIds[i]];
+ }
+ }
+ intent.putExtra(EXTRA_ATTRIBUTE_STRING_ARRAY, AttribStrings);
+ sendBroadcast(intent);
+ }
+
+ /**
+ * Returns the player supported value text for given IDs.
+ */
+ private void notifyAttributeValuesText(String what, int attribute,
+ byte [] valIds) {
+ Intent intent = new Intent(what);
+ String [] ValueStrings = new String [valIds.length];
+ intent.putExtra(EXTRA_GET_RESPONSE,GET_VALUE_TEXT);
+ intent.putExtra(EXTRA_ATTRIBUTE_ID, attribute);
+ Log.e(LOGTAG, "attrib is "+ attribute);
+ String [] valueStrs = null;
+ switch (attribute) {
+ case ATTRIBUTE_REPEATMODE:
+ valueStrs = new String[] {
+ "",
+ getString(R.string.repeat_off_notif),
+ getString(R.string.repeat_current_notif),
+ getString(R.string.repeat_all_notif),
+ };
+ break;
+ case ATTRIBUTE_SHUFFLEMODE:
+ valueStrs = new String[] {
+ "",
+ getString(R.string.shuffle_off_notif),
+ getString(R.string.shuffle_on_notif),
+ };
+ break;
+ }
+ for (int i = 0; i < valIds.length; i++) {
+ if ((valueStrs == null) ||
+ (valIds[i] >= valueStrs.length)) {
+ Log.e(LOGTAG, "value id is" + valIds[i] + "which is not supported");
+ ValueStrings[i] = "";
+ } else {
+ ValueStrings[i] = valueStrs[valIds[i]];
+ }
+ }
+ intent.putExtra(EXTRA_VALUE_STRING_ARRAY, ValueStrings);
+ sendBroadcast(intent);
+ }
+
+ /**
+ * Returns the player current values for given attrib IDs.
+ */
+ private void notifyAttributeValues(String what, HashMap<Byte, Boolean> attrIds, int extra) {
+ Intent intent = new Intent(what);
+ intent.putExtra(EXTRA_GET_RESPONSE, extra);
+ int j = 0;
+ byte [] retValarray = new byte [attrIds.size()*2];
+ for (int i = 0; i < attrIds.size()*2; i++) {
+ retValarray[i] = 0x0;
+ }
+
+ for (Byte attribute : attrIds.keySet()) {
+ if(attrIds.get(attribute)) {
+ retValarray[j] = attribute;
+ if (attribute == ATTRIBUTE_REPEATMODE) {
+ retValarray[j+1] = getMappingRepeatVal(mRepeatMode);
+ } else if (attribute == ATTRIBUTE_SHUFFLEMODE) {
+ retValarray[j+1] = getMappingShuffleVal(mShuffleMode);
+ }
+ j += 2;
+ } else {
+ retValarray[j] = attribute;
+ retValarray[j+1] = ERROR_NOTSUPPORTED;
+ j += 2;
+ }
+ }
+ intent.putExtra(EXTRA_ATTRIB_VALUE_PAIRS, retValarray);
+ sendBroadcast(intent);
+ }
+
+ /**
+ * Sets the values to current player for given attrib IDs.
+ */
+ private void setValidAttributes(byte [] attribValuePairs) {
+ byte attrib, value;
+
+ for (int i = 0; i < (attribValuePairs.length-1); i += 2) {
+ attrib = attribValuePairs[i];
+ value = attribValuePairs[i+1];
+ switch(attrib) {
+ case ATTRIBUTE_REPEATMODE:
+ if (isValidRepeatMode(value)) {
+ setRepeatMode(getMappingRepeatMode(value));
+ }
+ break;
+ case ATTRIBUTE_SHUFFLEMODE:
+ if (isValidShuffleMode(value)) {
+ setShuffleMode(getMappingShuffleMode(value));
+ }
+ break;
+ default:
+ Log.e(LOGTAG,"Unknown attribute"+attrib);
+ notifyAttributeValues(PLAYERSETTINGS_RESPONSE,
+ mAttributePairs, SET_ATTRIBUTE_VALUES);
+ break;
+ }
+ }
+ }
+
+ byte getMappingRepeatVal (int repeatMode) {
+ switch (repeatMode) {
+ case REPEAT_NONE:
+ return VALUE_REPEATMODE_OFF;
+ case REPEAT_CURRENT:
+ return VALUE_REPEATMODE_SINGLE;
+ case REPEAT_ALL:
+ return VALUE_REPEATMODE_ALL;
+ default:
+ return VALUE_REPEATMODE_OFF;
+ }
+ }
+
+ byte getMappingShuffleVal (int shuffleMode) {
+ switch (shuffleMode) {
+ case SHUFFLE_NONE:
+ return VALUE_SHUFFLEMODE_OFF;
+ case SHUFFLE_NORMAL:
+ return VALUE_SHUFFLEMODE_ALL;
+ case SHUFFLE_AUTO:
+ return VALUE_SHUFFLEMODE_ALL;
+ default:
+ return VALUE_SHUFFLEMODE_OFF;
+ }
+ }
+
+ int getMappingRepeatMode (byte repeatVal) {
+ switch (repeatVal) {
+ case VALUE_REPEATMODE_OFF:
+ return REPEAT_NONE;
+ case VALUE_REPEATMODE_SINGLE:
+ return REPEAT_CURRENT;
+ case VALUE_REPEATMODE_ALL:
+ case VALUE_REPEATMODE_GROUP:
+ return REPEAT_ALL;
+ default:
+ return REPEAT_NONE;
+ }
+ }
+
+ int getMappingShuffleMode (byte shuffleVal) {
+ switch (shuffleVal) {
+ case VALUE_SHUFFLEMODE_OFF:
+ return SHUFFLE_NONE;
+ case VALUE_SHUFFLEMODE_ALL:
+ case VALUE_SHUFFLEMODE_GROUP:
+ return SHUFFLE_NORMAL;
+ default:
+ return SHUFFLE_NONE;
+ }
+ }
+
+ /**
+ * Validates the value with CMDSET for Repeat mode.
+ */
+ private boolean isValidRepeatMode(byte value) {
+ if (value == 0) {
+ return false;
+ }
+ value--;
+ if ((value >= REPEAT_NONE) && ( value <= REPEAT_ALL)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Validates the value with CMDSET for Shuffle mode.
+ */
+ private boolean isValidShuffleMode(byte value) {
+ if (value == 0) {
+ return false;
+ }
+ value--;
+ // check the mapping for local suffle and argument
+ if ((value >= SHUFFLE_NONE) && ( value <= SHUFFLE_AUTO)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Provides a unified interface for dealing with midi files and
* other media files.
*/