From 1da0d4554261ba18e8e4818f2e4186c6cfd1c3ad Mon Sep 17 00:00:00 2001
From: Joshua Bahnsen <archrival@gmail.com>
Date: Wed, 17 Apr 2013 23:18:59 -0700
Subject: [PATCH] Add AVRCP and resume playback between sessions

---
 AndroidManifest.xml                           | 199 ++++++++++--------
 .../receiver/A2dpIntentReceiver.java          |  47 +++++
 .../service/DownloadServiceImpl.java          | 100 ++++++---
 .../ultrasonic/androidapp/util/FileUtil.java  |  11 +-
 .../ultrasonic/androidapp/util/Util.java      |  89 ++++++++
 5 files changed, 329 insertions(+), 117 deletions(-)
 create mode 100644 src/com/thejoshwa/ultrasonic/androidapp/receiver/A2dpIntentReceiver.java

diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 91a25d98..58fbf74d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1,91 +1,108 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:a="http://schemas.android.com/apk/res/android"
-          package="com.thejoshwa.ultrasonic.androidapp"
-          a:versionCode="4"
-          a:versionName="1.0.0.28" a:installLocation="auto">
+    package="com.thejoshwa.ultrasonic.androidapp"
+    a:installLocation="auto"
+    a:versionCode="4"
+    a:versionName="1.0.0.28" >
 
-    <uses-permission a:name="android.permission.INTERNET"/>
-    <uses-permission a:name="android.permission.READ_PHONE_STATE"/>
-    <uses-permission a:name="android.permission.ACCESS_NETWORK_STATE"/>
-    <uses-permission a:name="android.permission.WAKE_LOCK"/>
-    <uses-permission a:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
-    <uses-permission a:name="android.permission.RECORD_AUDIO"/>
-    <uses-permission a:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
+    <uses-permission a:name="android.permission.INTERNET" />
+    <uses-permission a:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission a:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission a:name="android.permission.WAKE_LOCK" />
+    <uses-permission a:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission a:name="android.permission.RECORD_AUDIO" />
+    <uses-permission a:name="android.permission.MODIFY_AUDIO_SETTINGS" />
 
-    <uses-sdk a:minSdkVersion="14" a:targetSdkVersion="17"/>
+    <uses-sdk
+        a:minSdkVersion="14"
+        a:targetSdkVersion="17" />
 
-    <supports-screens a:anyDensity="true" a:xlargeScreens="true" a:largeScreens="true" a:normalScreens="true" a:smallScreens="true"/>
+    <supports-screens
+        a:anyDensity="true"
+        a:largeScreens="true"
+        a:normalScreens="true"
+        a:smallScreens="true"
+        a:xlargeScreens="true" />
 
-    <application a:label="@string/common.appname" a:icon="@drawable/ic_launcher" a:allowBackup="false">
-
-        <activity a:name="com.thejoshwa.ultrasonic.androidapp.activity.MainActivity"
-                  a:label="UltraSonic"
-                  a:configChanges="orientation|keyboardHidden"
-                  a:launchMode="standard">
+    <application
+        a:allowBackup="false"
+        a:icon="@drawable/ic_launcher"
+        a:label="@string/common.appname" >
+        <activity
+            a:name=".activity.MainActivity"
+            a:configChanges="orientation|keyboardHidden"
+            a:label="UltraSonic"
+            a:launchMode="standard" >
             <intent-filter>
-                <action a:name="android.intent.action.MAIN"/>
-                <category a:name="android.intent.category.LAUNCHER"/>
+                <action a:name="android.intent.action.MAIN" />
+
+                <category a:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-
-        <activity a:name="com.thejoshwa.ultrasonic.androidapp.activity.SelectArtistActivity"
-                  a:configChanges="orientation|keyboardHidden"
-                  a:launchMode="standard"/>
-
-        <activity a:name="com.thejoshwa.ultrasonic.androidapp.activity.SelectAlbumActivity"
-                  a:configChanges="orientation|keyboardHidden"/>
-
-        <activity a:name="com.thejoshwa.ultrasonic.androidapp.activity.SearchActivity"
-                  a:label="@string/search.label"
-                  a:configChanges="orientation|keyboardHidden"
-                  a:launchMode="singleTask"
-                  />
-
-        <activity a:name="com.thejoshwa.ultrasonic.androidapp.activity.SelectPlaylistActivity"
-                  a:label="@string/playlist.label"
-                  a:configChanges="orientation|keyboardHidden"
-                  a:launchMode="standard"/>
-
-        <activity a:name="com.thejoshwa.ultrasonic.androidapp.activity.DownloadActivity"
-                  a:configChanges="keyboardHidden"
-                  a:launchMode="singleTask"
-                  a:theme="@android:style/Theme.NoTitleBar.Fullscreen"
-                  />
-        
-        <activity a:name="com.thejoshwa.ultrasonic.androidapp.activity.SettingsActivity"
-                  a:configChanges="orientation|keyboardHidden"
-                  a:launchMode="singleTask"/>
-
-        <activity a:name="com.thejoshwa.ultrasonic.androidapp.activity.HelpActivity"
-                  a:label="@string/help.label"
-                  a:launchMode="singleTask"/>
-
-        <activity a:name="com.thejoshwa.ultrasonic.androidapp.activity.LyricsActivity"
-                  a:configChanges="orientation|keyboardHidden"
-                  a:launchMode="singleTask"/>
-
-        <activity a:name="com.thejoshwa.ultrasonic.androidapp.activity.EqualizerActivity"
-                  a:label="@string/equalizer.label"
-                  a:configChanges="orientation|keyboardHidden"
-                  a:launchMode="singleTask"/>
-        
-        <activity a:name="com.thejoshwa.ultrasonic.androidapp.activity.VoiceQueryReceiverActivity"
-                  a:launchMode="singleTask">
+        <activity
+            a:name=".activity.SelectArtistActivity"
+            a:configChanges="orientation|keyboardHidden"
+            a:launchMode="standard" />
+        <activity
+            a:name=".activity.SelectAlbumActivity"
+            a:configChanges="orientation|keyboardHidden" />
+        <activity
+            a:name=".activity.SearchActivity"
+            a:configChanges="orientation|keyboardHidden"
+            a:label="@string/search.label"
+            a:launchMode="singleTask" />
+        <activity
+            a:name=".activity.SelectPlaylistActivity"
+            a:configChanges="orientation|keyboardHidden"
+            a:label="@string/playlist.label"
+            a:launchMode="standard" />
+        <activity
+            a:name=".activity.DownloadActivity"
+            a:configChanges="keyboardHidden"
+            a:launchMode="singleTask"
+            a:theme="@android:style/Theme.NoTitleBar.Fullscreen" />
+        <activity
+            a:name=".activity.SettingsActivity"
+            a:configChanges="orientation|keyboardHidden"
+            a:launchMode="singleTask" />
+        <activity
+            a:name=".activity.HelpActivity"
+            a:label="@string/help.label"
+            a:launchMode="singleTask" />
+        <activity
+            a:name=".activity.LyricsActivity"
+            a:configChanges="orientation|keyboardHidden"
+            a:launchMode="singleTask" />
+        <activity
+            a:name=".activity.EqualizerActivity"
+            a:configChanges="orientation|keyboardHidden"
+            a:label="@string/equalizer.label"
+            a:launchMode="singleTask" />
+        <activity
+            a:name=".activity.VoiceQueryReceiverActivity"
+            a:launchMode="singleTask" >
             <intent-filter>
                 <action a:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
+
                 <category a:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
-
-        <activity a:name="com.thejoshwa.ultrasonic.androidapp.activity.QueryReceiverActivity"
-                  a:launchMode="singleTask">
+        <activity
+            a:name=".activity.QueryReceiverActivity"
+            a:launchMode="singleTask" >
             <intent-filter>
-                <action a:name="android.intent.action.SEARCH"/>
+                <action a:name="android.intent.action.SEARCH" />
             </intent-filter>
-            <meta-data a:name="android.app.searchable" a:resource="@xml/searchable"/>
+
+            <meta-data
+                a:name="android.app.searchable"
+                a:resource="@xml/searchable" />
         </activity>
-        
-        <service a:name="com.thejoshwa.ultrasonic.androidapp.service.DownloadServiceImpl" a:label="UltraSonic Download Service">
+
+        <service
+            a:name=".service.DownloadServiceImpl"
+            a:label="UltraSonic Download Service"
+            a:exported="false" >
             <intent-filter>
                 <action a:name="com.thejoshwa.ultrasonic.androidapp.CMD_TOGGLEPAUSE" />
                 <action a:name="com.thejoshwa.ultrasonic.androidapp.CMD_PLAY" />
@@ -96,31 +113,43 @@
             </intent-filter>
         </service>
 
-        <receiver a:name="com.thejoshwa.ultrasonic.androidapp.receiver.MediaButtonIntentReceiver">
-            <intent-filter a:priority="2147483647">
+        <receiver a:name=".receiver.MediaButtonIntentReceiver" >
+            <intent-filter a:priority="2147483647" >
                 <action a:name="android.intent.action.MEDIA_BUTTON" />
             </intent-filter>
         </receiver>
-
-        <receiver a:name="com.thejoshwa.ultrasonic.androidapp.receiver.BluetoothIntentReceiver">
+        <receiver a:name=".receiver.BluetoothIntentReceiver" >
             <intent-filter>
-                <action a:name="android.bluetooth.a2dp.action.SINK_STATE_CHANGED"/>
+                <action a:name="android.bluetooth.a2dp.action.SINK_STATE_CHANGED" />
             </intent-filter>
         </receiver>
-
-        <receiver a:name="com.thejoshwa.ultrasonic.androidapp.provider.UltraSonicAppWidgetProvider4x1" a:label="UltraSonic (4x1)" >
+        <receiver
+            a:name=".provider.UltraSonicAppWidgetProvider4x1"
+            a:label="UltraSonic (4x1)" >
             <intent-filter>
-                <action a:name="android.appwidget.action.APPWIDGET_UPDATE"/>
+                <action a:name="android.appwidget.action.APPWIDGET_UPDATE" />
             </intent-filter>
-            <meta-data a:name="android.appwidget.provider" a:resource="@xml/appwidget_info_4x1"/>
+
+            <meta-data
+                a:name="android.appwidget.provider"
+                a:resource="@xml/appwidget_info_4x1" />
         </receiver>
 
-        <provider a:name="com.thejoshwa.ultrasonic.androidapp.provider.SearchSuggestionProvider"
-                  a:authorities="com.thejoshwa.ultrasonic.androidapp.provider.SearchSuggestionProvider"/>
+        <provider
+            a:name=".provider.SearchSuggestionProvider"
+            a:authorities="com.thejoshwa.ultrasonic.androidapp.provider.SearchSuggestionProvider" />
 
-        <meta-data a:name="android.app.default_searchable"
-                   a:value="com.thejoshwa.ultrasonic.androidapp.activity.QueryReceiverActivity"/>
+        <meta-data
+            a:name="android.app.default_searchable"
+            a:value="com.thejoshwa.ultrasonic.androidapp.activity.QueryReceiverActivity" />
 
+        <receiver
+            a:name=".receiver.A2dpIntentReceiver"
+            a:exported="false" >
+            <intent-filter>
+                <action a:name="com.android.music.playstatusrequest" />
+            </intent-filter>
+        </receiver>
     </application>
 
-</manifest>
+</manifest>
\ No newline at end of file
diff --git a/src/com/thejoshwa/ultrasonic/androidapp/receiver/A2dpIntentReceiver.java b/src/com/thejoshwa/ultrasonic/androidapp/receiver/A2dpIntentReceiver.java
new file mode 100644
index 00000000..2a762b9f
--- /dev/null
+++ b/src/com/thejoshwa/ultrasonic/androidapp/receiver/A2dpIntentReceiver.java
@@ -0,0 +1,47 @@
+package com.thejoshwa.ultrasonic.androidapp.receiver;
+
+import com.thejoshwa.ultrasonic.androidapp.domain.MusicDirectory.Entry;
+import com.thejoshwa.ultrasonic.androidapp.service.DownloadService;
+import com.thejoshwa.ultrasonic.androidapp.service.DownloadServiceImpl;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class A2dpIntentReceiver extends BroadcastReceiver {
+	
+    private static final String PLAYSTATUS_REQUEST = "com.android.music.playstatusrequest";
+    private static final String PLAYSTATUS_RESPONSE = "com.android.music.playstatusresponse";
+
+	@Override
+	public void onReceive(Context context, Intent intent) {
+		
+		DownloadService downloadService = DownloadServiceImpl.getInstance();
+		
+		if (downloadService != null){
+			Intent avrcpIntent = new Intent(PLAYSTATUS_RESPONSE);
+			
+			avrcpIntent.putExtra("duration", (long) downloadService.getCurrentPlaying().getSong().getDuration());
+			avrcpIntent.putExtra("position", (long) downloadService.getPlayerPosition());
+			avrcpIntent.putExtra("ListSize", (long) downloadService.getDownloads().size());
+						
+			switch (downloadService.getPlayerState()){
+				case STARTED:
+	            	avrcpIntent.putExtra("playing", true);
+	                break;
+	            case STOPPED:
+	            	avrcpIntent.putExtra("playing", false);
+	                break;
+	            case PAUSED:
+	            	avrcpIntent.putExtra("playing", false);
+	                break;
+	            case COMPLETED:
+	            	avrcpIntent.putExtra("playing", false);
+	                break;
+	            default:
+	                return;
+			}			
+		
+			context.sendBroadcast(avrcpIntent);
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/com/thejoshwa/ultrasonic/androidapp/service/DownloadServiceImpl.java b/src/com/thejoshwa/ultrasonic/androidapp/service/DownloadServiceImpl.java
index f4b5ea74..8c139e0e 100644
--- a/src/com/thejoshwa/ultrasonic/androidapp/service/DownloadServiceImpl.java
+++ b/src/com/thejoshwa/ultrasonic/androidapp/service/DownloadServiceImpl.java
@@ -74,6 +74,9 @@ public class DownloadServiceImpl extends Service implements DownloadService {
     public static final String CMD_STOP = "com.thejoshwa.ultrasonic.androidapp.CMD_STOP";
     public static final String CMD_PREVIOUS = "com.thejoshwa.ultrasonic.androidapp.CMD_PREVIOUS";
     public static final String CMD_NEXT = "com.thejoshwa.ultrasonic.androidapp.CMD_NEXT";
+    
+    public static final String PLAYSTATUS_REQUEST = "com.android.music.playstatusrequest";
+    public static final String PLAYSTATUS_RESPONSE = "com.android.music.playstatusresponse";
 
     private final IBinder binder = new SimpleServiceBinder<DownloadService>(this);
     private MediaPlayer mediaPlayer;
@@ -433,11 +436,12 @@ public class DownloadServiceImpl extends Service implements DownloadService {
         
         if (currentPlaying != null) {
         	Util.broadcastNewTrackInfo(this, currentPlaying.getSong());
+        	Util.broadcastA2dpMetaDataChange(this, getInstance());
         } else {
             Util.broadcastNewTrackInfo(this, null);
         }
         
-        setRemoteControl();
+        updateRemoteControl();
 
         // Update widget
         UltraSonicAppWidgetProvider4x1.getInstance().notifyChange(this, this, playerState == PlayerState.STARTED);
@@ -474,6 +478,7 @@ public class DownloadServiceImpl extends Service implements DownloadService {
     {
         int current = getCurrentPlayingIndex();
         if (current == -1) {
+        	resetProgressBar();
             play(0);
         } else {
             play(current);
@@ -538,8 +543,10 @@ public class DownloadServiceImpl extends Service implements DownloadService {
 
         // Restart song if played more than five seconds.
         if (getPlayerPosition() > 5000 || index == 0) {
+        	resetProgressBar();
             play(index);
         } else {
+        	resetProgressBar();
             play(index - 1);
         }
     }
@@ -548,12 +555,24 @@ public class DownloadServiceImpl extends Service implements DownloadService {
     public synchronized void next() {
         int index = getCurrentPlayingIndex();
         if (index != -1) {
+        	resetProgressBar();
             play(index + 1);
         }
     }
+    
+    private void resetProgressBar()
+    {
+    	SeekBar progressBar = DownloadActivity.getProgressBar();
+    	if (progressBar != null) {
+    		progressBar.setProgress(0);
+    	}
+    	secondaryProgress = -1;
+    }
 
     private void onSongCompleted() {
+    	resetProgressBar();
         int index = getCurrentPlayingIndex();
+        
         if (index != -1) {
             switch (getRepeatMode()) {
                 case OFF:
@@ -593,6 +612,8 @@ public class DownloadServiceImpl extends Service implements DownloadService {
     	
         try {
             if (playerState == STARTED) {
+            	resetProgressBar();
+            	
                 if (jukeboxEnabled) {
                     jukeboxService.stop();
                 } else {
@@ -643,7 +664,7 @@ public class DownloadServiceImpl extends Service implements DownloadService {
             if (jukeboxEnabled) {
                 return jukeboxService.getPositionSeconds() * 1000;
             } else {
-                return mediaPlayer.getCurrentPosition();
+           		return mediaPlayer.getCurrentPosition();
             }
         } catch (Exception x) {
             handleError(x);
@@ -677,35 +698,40 @@ public class DownloadServiceImpl extends Service implements DownloadService {
     synchronized void setPlayerState(PlayerState playerState) {
         Log.i(TAG, this.playerState.name() + " -> " + playerState.name() + " (" + currentPlaying + ")");
 
-        if (playerState == PAUSED) {
+        this.playerState = playerState;
+        
+        if (this.playerState == PAUSED) {
             lifecycleSupport.serializeDownloadQueue();
         }
         
-        boolean showWhenPaused = (playerState == PlayerState.PAUSED && Util.isNotificationAlwaysEnabled(this));
-
-        boolean show = playerState == PlayerState.STARTED || showWhenPaused;
-        boolean hide = playerState == PlayerState.IDLE || playerState == PlayerState.STOPPED || !showWhenPaused;
-        Util.broadcastPlaybackStatusChange(this, playerState);
-
-        this.playerState = playerState;
         if (this.playerState == PlayerState.STARTED) {
     		audioManager.requestAudioFocus(_afChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
         }
         
-        setRemoteControl();
+        boolean showWhenPaused = (this.playerState == PlayerState.PAUSED && Util.isNotificationAlwaysEnabled(this));
+
+        boolean show = this.playerState == PlayerState.STARTED || showWhenPaused;
+        boolean hide = this.playerState == PlayerState.IDLE || this.playerState == PlayerState.STOPPED || !showWhenPaused;
+        Util.broadcastPlaybackStatusChange(this, this.playerState);
+        Util.broadcastA2dpPlayStatusChange(this, this.playerState, getInstance());
+
+        // Set remote control
+       updateRemoteControl();
         
         // Update widget
-        UltraSonicAppWidgetProvider4x1.getInstance().notifyChange(this, this, playerState == PlayerState.STARTED);
+        UltraSonicAppWidgetProvider4x1.getInstance().notifyChange(this, this, this.playerState == PlayerState.STARTED);
         
        	if (show) {
-       		Util.showPlayingNotification(this, this, handler, currentPlaying.getSong(), this.notification, this.playerState);
+       		if (currentPlaying != null) {
+       			Util.showPlayingNotification(this, this, handler, currentPlaying.getSong(), this.notification, this.playerState);
+       		}
        	} else if (hide) {
        		Util.hidePlayingNotification(this, this, handler);
        	}
         
-        if (playerState == STARTED) {
+        if (this.playerState == STARTED) {
             scrobbler.scrobble(this, currentPlaying, false);
-        } else if (playerState == COMPLETED) {
+        } else if (this.playerState == COMPLETED) {
             scrobbler.scrobble(this, currentPlaying, true);
         }
     }
@@ -754,7 +780,7 @@ public class DownloadServiceImpl extends Service implements DownloadService {
         jukeboxService.adjustVolume(up);
     }
 
-    private void setRemoteControl() {
+    private void updateRemoteControl() {
     	if (Util.isLockScreenEnabled(this)) {
         	if (remoteControlClient == null) {
         		Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
@@ -828,7 +854,14 @@ public class DownloadServiceImpl extends Service implements DownloadService {
     private synchronized void bufferAndPlay() {
         reset();
 
-        bufferTask = new BufferTask(currentPlaying, 0);
+        int progressBarProgress = 0;
+        SeekBar progressBar = DownloadActivity.getProgressBar();
+        
+        if (progressBar != null) {
+        	progressBarProgress = progressBar.getProgress();
+        }
+        
+        bufferTask = new BufferTask(currentPlaying, progressBarProgress);
         bufferTask.start();
     }
 
@@ -1083,21 +1116,27 @@ public class DownloadServiceImpl extends Service implements DownloadService {
         private final File partialFile;
 
         public BufferTask(DownloadFile downloadFile, int position) {
-            this.downloadFile = downloadFile;
-            this.position = position;
-            partialFile = downloadFile.getPartialFile();
-            int bufferLength = downloadFile.getBufferLength();
+    		this.downloadFile = downloadFile;
+    		this.position = position;
+    		partialFile = downloadFile.getPartialFile();
 
-			// Calculate roughly how many bytes buffer length corresponds to.
-			int bitRate = downloadFile.getBitRate();
-			long byteCount = Math.max(100000, bitRate * 1024 / 8 * bufferLength);
+        	if (!Util.isStreamProxyEnabled(getBaseContext())) {
+        		int bufferLength = downloadFile.getBufferLength();
 
-			// Find out how large the file should grow before resuming playback.
-			if (position == 0) {
-				expectedFileSize = byteCount;
-			} else {
-				expectedFileSize = partialFile.length() + byteCount;
-			}
+        		// Calculate roughly how many bytes buffer length corresponds to.
+        		int bitRate = downloadFile.getBitRate();
+        		long byteCount = Math.max(100000, bitRate * 1024 / 8 * bufferLength);
+
+        		// 	Find out how large the file should grow before resuming playback.
+        		if (position == 0) {
+        			expectedFileSize = byteCount;
+        		} else {
+        			expectedFileSize = partialFile.length() + byteCount;
+        		}
+        	} else {
+        		Log.i(TAG, "StreamProxy is enabled, will let media player control buffer size");
+        		expectedFileSize = 0;
+        	}
         }
 
         @Override
@@ -1111,7 +1150,6 @@ public class DownloadServiceImpl extends Service implements DownloadService {
                         return;
                     }
                 }
-
         	}
         	
             doPlay(downloadFile, position, true);
diff --git a/src/com/thejoshwa/ultrasonic/androidapp/util/FileUtil.java b/src/com/thejoshwa/ultrasonic/androidapp/util/FileUtil.java
index 87bc32e9..eeb73882 100644
--- a/src/com/thejoshwa/ultrasonic/androidapp/util/FileUtil.java
+++ b/src/com/thejoshwa/ultrasonic/androidapp/util/FileUtil.java
@@ -88,7 +88,16 @@ public class FileUtil {
         if (albumArtFile.exists()) {
             Bitmap bitmap = BitmapFactory.decodeFile(albumArtFile.getPath());
             Log.i("getAlbumArtBitmap", String.valueOf(size));
-            return bitmap == null ? null : Util.scaleBitmap(bitmap, size); 
+            if (bitmap == null) {
+            	return null;
+            }
+            else {
+            	if (size > 0) {
+            		return Util.scaleBitmap(bitmap, size);
+            	}
+            	
+            	return bitmap;
+            }
         }
         
         return null;
diff --git a/src/com/thejoshwa/ultrasonic/androidapp/util/Util.java b/src/com/thejoshwa/ultrasonic/androidapp/util/Util.java
index 8a4c8586..01b5d6a7 100644
--- a/src/com/thejoshwa/ultrasonic/androidapp/util/Util.java
+++ b/src/com/thejoshwa/ultrasonic/androidapp/util/Util.java
@@ -38,6 +38,7 @@ import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.Parcelable;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.KeyEvent;
@@ -54,6 +55,7 @@ import com.thejoshwa.ultrasonic.androidapp.domain.Version;
 import com.thejoshwa.ultrasonic.androidapp.domain.MusicDirectory.Entry;
 import com.thejoshwa.ultrasonic.androidapp.provider.UltraSonicAppWidgetProvider4x1;
 import com.thejoshwa.ultrasonic.androidapp.receiver.MediaButtonIntentReceiver;
+import com.thejoshwa.ultrasonic.androidapp.service.DownloadService;
 import com.thejoshwa.ultrasonic.androidapp.service.DownloadServiceImpl;
 import org.apache.http.HttpEntity;
 
@@ -93,6 +95,9 @@ public class Util extends DownloadActivity {
 
     public static final String EVENT_META_CHANGED = "com.thejoshwa.ultrasonic.androidapp.EVENT_META_CHANGED";
     public static final String EVENT_PLAYSTATE_CHANGED = "com.thejoshwa.ultrasonic.androidapp.EVENT_PLAYSTATE_CHANGED";
+    
+    public static final String CM_AVRCP_PLAYSTATE_CHANGED = "com.android.music.playstatechanged";
+    public static final String CM_AVRCP_METADATA_CHANGED = "com.android.music.metachanged";
 
     private static final Map<Integer, Version> SERVER_REST_VERSIONS = new ConcurrentHashMap<Integer, Version>();
 
@@ -817,6 +822,90 @@ public class Util extends DownloadActivity {
 
         context.sendBroadcast(intent);
     }
+    
+	public static void broadcastA2dpMetaDataChange(Context context,	DownloadService downloadService) {
+		if (downloadService != null) {
+			MusicDirectory.Entry song = downloadService.getCurrentPlaying().getSong();
+
+			Intent avrcpIntent = new Intent(CM_AVRCP_METADATA_CHANGED);
+
+			if (song != null) {
+				Bitmap bitmap = FileUtil.getAlbumArtBitmap(context, song, 0);
+				
+				avrcpIntent.putExtra("track", song.getTitle());
+				avrcpIntent.putExtra("track_name", song.getTitle());
+				avrcpIntent.putExtra("artist", song.getArtist());
+				avrcpIntent.putExtra("artist_name", song.getArtist());
+				avrcpIntent.putExtra("album", song.getAlbum());
+				avrcpIntent.putExtra("album_name", song.getAlbum());
+				avrcpIntent.putExtra("cover", (Parcelable) bitmap);
+				avrcpIntent.putExtra("coverart", (Parcelable) bitmap);
+				avrcpIntent.putExtra("ListSize", (long) downloadService.getDownloads().size());
+				avrcpIntent.putExtra("id", (long) downloadService.getCurrentPlayingIndex() + 1);
+				avrcpIntent.putExtra("duration", (long) song.getDuration());
+				avrcpIntent.putExtra("position", (long) downloadService.getPlayerPosition());
+
+			} else {
+				avrcpIntent.putExtra("track", "");
+				avrcpIntent.putExtra("track_name", "");
+				avrcpIntent.putExtra("artist", "");
+				avrcpIntent.putExtra("artist_name", "");
+				avrcpIntent.putExtra("album", "");
+				avrcpIntent.putExtra("album_name", "");
+				avrcpIntent.putExtra("cover", (Parcelable) null);
+				avrcpIntent.putExtra("coverart", (Parcelable) null);
+				avrcpIntent.putExtra("ListSize", (long) 0);
+				avrcpIntent.putExtra("id", (long) 0);
+				avrcpIntent.putExtra("duration", (long) 0);
+				avrcpIntent.putExtra("position", (long) 0);
+			}
+
+			context.sendBroadcast(avrcpIntent);
+		}
+
+	}
+
+	public static void broadcastA2dpPlayStatusChange(Context context, PlayerState state, DownloadService downloadService) {
+
+		if (downloadService.getCurrentPlaying() != null) {
+			Intent avrcpIntent = new Intent(CM_AVRCP_PLAYSTATE_CHANGED);
+
+			MusicDirectory.Entry song = downloadService.getCurrentPlaying().getSong();
+			Bitmap bitmap = FileUtil.getAlbumArtBitmap(context, song, 0);
+			
+			avrcpIntent.putExtra("track", song.getTitle());
+			avrcpIntent.putExtra("track_name", song.getTitle());
+			avrcpIntent.putExtra("artist", song.getArtist());
+			avrcpIntent.putExtra("artist_name", song.getArtist());
+			avrcpIntent.putExtra("album", song.getAlbum());
+			avrcpIntent.putExtra("album_name", song.getAlbum());
+			avrcpIntent.putExtra("cover", (Parcelable) bitmap);
+			avrcpIntent.putExtra("coverart", (Parcelable) bitmap);
+			avrcpIntent.putExtra("ListSize", (long) downloadService.getDownloads().size());
+			avrcpIntent.putExtra("id", (long) downloadService.getCurrentPlayingIndex() + 1);
+			avrcpIntent.putExtra("duration", (long) song.getDuration());
+			avrcpIntent.putExtra("position", (long) downloadService.getPlayerPosition());
+
+			switch (state) {
+				case STARTED:
+					avrcpIntent.putExtra("playing", true);
+					break;
+				case STOPPED:
+					avrcpIntent.putExtra("playing", false);
+					break;
+				case PAUSED:
+					avrcpIntent.putExtra("playing", false);
+					break;
+				case COMPLETED:
+					avrcpIntent.putExtra("playing", false);
+					break;
+				default:
+					return; // No need to broadcast.
+			}
+			
+			context.sendBroadcast(avrcpIntent);
+		}
+	}
 
     /**
      * <p>Broadcasts the given player state as the one being set.</p>