@Override
 public void onPlayerHaterShutdown() {
   mHandler.removeTargettedMessages(SERVICE_STOPPING);
   if (shouldHandleMessage(SERVICE_STOPPING)) {
     mPlugin.onPlayerHaterShutdown();
   } else {
     mHandler.sendTargettedEmptyMessage(SERVICE_STOPPING);
   }
 }
 @Override
 public void onSongChanged(Song song) {
   mHandler.removeTargettedMessages(CHANGES_COMPLETE_INTERNAL);
   if (shouldHandleMessage(SONG_CHANGED)) {
     mPlugin.onSongChanged(song);
   } else {
     mHandler.obtainTargettedMessage(SONG_CHANGED, song).sendToTarget();
   }
   mHandler.sendTargettedEmptyMessage(CHANGES_COMPLETE_INTERNAL);
 }
 @Override
 public void onSongFinished(Song song, int reason) {
   mHandler.removeTargettedMessages(CHANGES_COMPLETE_INTERNAL);
   if (shouldHandleMessage(SONG_FINISHED)) {
     mPlugin.onSongFinished(song, reason);
   } else {
     mHandler.obtainTargettedMessage(SONG_FINISHED, reason, 0, song).sendToTarget();
   }
   mHandler.sendTargettedEmptyMessage(CHANGES_COMPLETE_INTERNAL);
 }
 @Override
 public void onPendingIntentChanged(PendingIntent pending) {
   mHandler.removeTargettedMessages(CHANGES_COMPLETE_INTERNAL);
   if (shouldHandleMessage(INTENT_CHANGED)) {
     mPlugin.onPendingIntentChanged(pending);
   } else {
     mHandler.obtainTargettedMessage(INTENT_CHANGED, pending).sendToTarget();
   }
   mHandler.sendTargettedEmptyMessage(CHANGES_COMPLETE_INTERNAL);
 }
 @Override
 public void onNextSongUnavailable() {
   mHandler.removeTargettedMessages(CHANGES_COMPLETE_INTERNAL);
   if (shouldHandleMessage(NEXT_TRACK_UNAVAILABLE)) {
     mPlugin.onNextSongUnavailable();
   } else {
     mHandler.sendTargettedEmptyMessage(NEXT_TRACK_UNAVAILABLE);
   }
   mHandler.sendTargettedEmptyMessage(CHANGES_COMPLETE_INTERNAL);
 }
 @Override
 public void onNextSongAvailable(Song nextTrack) {
   mHandler.removeTargettedMessages(CHANGES_COMPLETE_INTERNAL);
   if (shouldHandleMessage(NEXT_TRACK_AVAILABLE)) {
     mPlugin.onNextSongAvailable(nextTrack);
   } else {
     mHandler.obtainTargettedMessage(NEXT_TRACK_AVAILABLE, nextTrack).sendToTarget();
   }
   mHandler.sendTargettedEmptyMessage(CHANGES_COMPLETE_INTERNAL);
 }
 @Override
 public void onAlbumArtChanged(Uri url) {
   mHandler.removeTargettedMessages(CHANGES_COMPLETE_INTERNAL);
   if (shouldHandleMessage(ART_CHANGED_URI)) {
     mPlugin.onAlbumArtChanged(url);
   } else {
     mHandler.obtainTargettedMessage(ART_CHANGED_URI, url).sendToTarget();
   }
   mHandler.sendTargettedEmptyMessage(CHANGES_COMPLETE_INTERNAL);
 }
 @Override
 public void onArtistChanged(String artist) {
   mHandler.removeTargettedMessages(CHANGES_COMPLETE_INTERNAL);
   if (shouldHandleMessage(ARTIST_CHANGED)) {
     mPlugin.onArtistChanged(artist);
   } else {
     mHandler.obtainTargettedMessage(ARTIST_CHANGED, artist).sendToTarget();
   }
   mHandler.sendTargettedEmptyMessage(CHANGES_COMPLETE_INTERNAL);
 }
 @Override
 public void onAlbumTitleChanged(String title) {
   mHandler.removeTargettedMessages(CHANGES_COMPLETE_INTERNAL);
   if (shouldHandleMessage(ALBUM_CHANGED)) {
     mPlugin.onAlbumTitleChanged(title);
   } else {
     mHandler.obtainTargettedMessage(ALBUM_CHANGED, title).sendToTarget();
   }
   mHandler.sendTargettedEmptyMessage(CHANGES_COMPLETE_INTERNAL);
 }
 @Override
 public void onAudioStopped() {
   mHandler.removeTargettedMessages(CHANGES_COMPLETE_INTERNAL);
   if (shouldHandleMessage(PLAYBACK_STOPPED)) {
     mPlugin.onAudioStopped();
   } else {
     mHandler.sendTargettedEmptyMessage(PLAYBACK_STOPPED);
   }
   mHandler.sendTargettedEmptyMessage(CHANGES_COMPLETE_INTERNAL);
 }
 @Override
 public void onAudioLoading() {
   mHandler.removeTargettedMessages(CHANGES_COMPLETE_INTERNAL);
   if (shouldHandleMessage(AUDIO_LOADING)) {
     mPlugin.onAudioLoading();
   } else {
     mHandler.sendTargettedEmptyMessage(AUDIO_LOADING);
   }
   mHandler.sendTargettedEmptyMessage(CHANGES_COMPLETE_INTERNAL);
 }
 @Override
 public void onDurationChanged(int duration) {
   mHandler.removeTargettedMessages(CHANGES_COMPLETE_INTERNAL);
   if (shouldHandleMessage(DURATION_CHANGED)) {
     mPlugin.onDurationChanged(duration);
   } else {
     mHandler.obtainTargettedMessage(DURATION_CHANGED, duration).sendToTarget();
   }
   mHandler.sendTargettedEmptyMessage(CHANGES_COMPLETE_INTERNAL);
 }
 @Override
 public void onTransportControlFlagsChanged(int transportControlFlags) {
   mHandler.removeTargettedMessages(CHANGES_COMPLETE_INTERNAL);
   if (shouldHandleMessage(TRANSPORT_CONTROL_FLAGS_CHANGED)) {
     mPlugin.onTransportControlFlagsChanged(transportControlFlags);
   } else {
     mHandler
         .obtainTargettedMessage(TRANSPORT_CONTROL_FLAGS_CHANGED, transportControlFlags, 0)
         .sendToTarget();
   }
   mHandler.sendTargettedEmptyMessage(CHANGES_COMPLETE_INTERNAL);
 }
 @Override
 public void onPlayerHaterLoaded(Context context, PlayerHater playerHater) {
   if (shouldHandleMessage(PLAYER_HATER_LOADED)) {
     mPlugin.onPlayerHaterLoaded(context, playerHater);
   } else {
     mHandler
         .obtainTargettedMessage(PLAYER_HATER_LOADED, new LoadedObject(context, playerHater))
         .sendToTarget();
   }
 }
 @Override
 public boolean handleMessage(Message msg) {
   switch (msg.what) {
     case CHANGES_COMPLETE_INTERNAL:
       if (!mHandler.hasTargettedMessages(CHANGES_COMPLETE)) {
         mHandler.sendTargettedEmptyMessage(CHANGES_COMPLETE);
       }
       break;
     case SONG_CHANGED:
       mPlugin.onSongChanged((Song) msg.obj);
       break;
     case DURATION_CHANGED:
       mPlugin.onDurationChanged((Integer) msg.obj);
       break;
     case NEXT_TRACK_UNAVAILABLE:
       mPlugin.onNextSongUnavailable();
       break;
     case NEXT_TRACK_AVAILABLE:
       mPlugin.onNextSongAvailable((Song) msg.obj);
       break;
     case ART_CHANGED_URI:
       mPlugin.onAlbumArtChanged((Uri) msg.obj);
       break;
     case ALBUM_CHANGED:
       mPlugin.onAlbumTitleChanged((String) msg.obj);
       break;
     case ARTIST_CHANGED:
       mPlugin.onArtistChanged((String) msg.obj);
       break;
     case TITLE_CHANGED:
       mPlugin.onTitleChanged((String) msg.obj);
       break;
     case PLAYBACK_STOPPED:
       mPlugin.onAudioStopped();
       break;
     case PLAYBACK_STARTED:
       mPlugin.onAudioStarted();
       break;
     case PLAYBACK_RESUMED:
       mPlugin.onAudioResumed();
       break;
     case PLAYBACK_PAUSED:
       mPlugin.onAudioPaused();
       break;
     case AUDIO_LOADING:
       mPlugin.onAudioLoading();
       break;
     case INTENT_CHANGED:
       mPlugin.onPendingIntentChanged((PendingIntent) msg.obj);
       break;
     case SONG_FINISHED:
       mPlugin.onSongFinished((Song) msg.obj, msg.arg1);
       break;
     case PLAYER_HATER_LOADED:
       LoadedObject o = (LoadedObject) msg.obj;
       mPlugin.onPlayerHaterLoaded(o.context, o.playerHater);
       break;
     case TRANSPORT_CONTROL_FLAGS_CHANGED:
       mPlugin.onTransportControlFlagsChanged(msg.arg1);
       break;
     case CHANGES_COMPLETE:
       mPlugin.onChangesComplete();
       break;
     case SERVICE_STOPPING:
       mPlugin.onPlayerHaterShutdown();
       break;
     default:
       return false;
   }
   return true;
 }