@SuppressLint({"InlinedApi", "NewApi"})
  public void startDownload(ClementineMessage message) {
    mItem = message.getMessage().getRequestDownloadSongs().getDownloadItem();

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
      this.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, message);
    } else {
      this.execute(message);
    }
  }
  @Override
  public void onReceive(Context context, Intent intent) {
    // Check if we have an media button intent
    if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {

      // Get the key event and obtain a new message
      KeyEvent event = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
      Message msg = Message.obtain();

      // Only on KeyDown
      if (event.getAction() == KeyEvent.ACTION_UP) {
        // Check which key was pressed
        switch (event.getKeyCode()) {
          case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
            msg.obj = ClementineMessage.getMessage(MsgType.PLAYPAUSE);
            break;
          case KeyEvent.KEYCODE_MEDIA_NEXT:
            msg.obj = ClementineMessage.getMessage(MsgType.NEXT);
            break;
          case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
            msg.obj = ClementineMessage.getMessage(MsgType.PREVIOUS);
            break;
          case KeyEvent.KEYCODE_VOLUME_DOWN:
            msg.obj = ClementineMessageFactory.buildVolumeMessage(App.mClementine.getVolume() - 10);
            break;
          case KeyEvent.KEYCODE_VOLUME_UP:
            msg.obj = ClementineMessageFactory.buildVolumeMessage(App.mClementine.getVolume() + 10);
            break;
          default:
            msg = null;
            break;
        }

        // Now send the message
        if (msg != null && App.mClementineConnection != null) {
          App.mClementineConnection.mHandler.sendMessage(msg);
        }
      }
    }
  }
        @Override
        public void onClick(View v) {
          Message msg = Message.obtain();

          switch (v.getId()) {
            case R.id.btnNext:
              msg.obj = ClementineMessage.getMessage(MsgType.NEXT);
              break;
            case R.id.btnPrev:
              msg.obj = ClementineMessage.getMessage(MsgType.PREVIOUS);
              break;
            case R.id.btnPlaypause:
              msg.obj = ClementineMessage.getMessage(MsgType.PLAYPAUSE);
              break;
            default:
              break;
          }
          // Send the request to the thread
          if (msg.obj != null) {
            App.mClementineConnection.mHandler.sendMessage(msg);
          }
        }
 /**
  * Send a request to clementine
  *
  * @param message The request as a RequestToThread object
  * @return true if data was sent, false if not
  */
 public boolean sendRequest(ClementineMessage message) {
   // Create the protocolbuffer
   byte[] data = message.getMessage().toByteArray();
   try {
     mOut.writeInt(data.length);
     mOut.write(data);
     mOut.flush();
   } catch (Exception e) {
     // Try to reconnect
     closeSocket();
     return false;
   }
   return true;
 }
  /**
   * Disconnect from Clementine
   *
   * @param message The RequestDisconnect Object
   */
  public void disconnect(ClementineMessage message) {
    if (isConnected()) {
      // Send the disconnect message to clementine
      byte[] data = message.getMessage().toByteArray();

      try {
        // Now send the data
        mOut.writeInt(data.length);
        mOut.write(data);
        mOut.flush();

        closeSocket();
      } catch (IOException e) {
      }
    }
  }
  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
      case R.id.library_menu_refresh:
        mAdapters.clear();
        showList();

        App.libraryDownloader = new ClementineLibraryDownloader(getActivity());
        App.libraryDownloader.addOnLibraryDownloadListener(mOnLibraryDownloadListener);
        App.libraryDownloader.startDownload(ClementineMessage.getMessage(MsgType.GET_LIBRARY));

        createDownloadProgressDialog();
        break;
      default:
        return super.onOptionsItemSelected(item);
    }

    return true;
  }
        @Override
        public boolean onLongClick(View v) {
          boolean ret = false;
          Message msg = Message.obtain();

          switch (v.getId()) {
            case R.id.btnPlaypause:
              Toast.makeText(getActivity(), R.string.player_stop_after_current, Toast.LENGTH_SHORT)
                  .show();
              msg.obj = ClementineMessage.getMessage(MsgType.STOP_AFTER);
              ret = true;
              break;
            default:
              break;
          }

          App.mClementineConnection.mHandler.sendMessage(msg);
          return ret;
        }
  /**
   * Try to connect to Clementine
   *
   * @param message The Request Object. Stores the ip to connect to.
   */
  @Override
  public boolean createConnection(ClementineMessage message) {
    boolean connected = false;
    // Reset the connected flag
    mLastKeepAlive = 0;

    // Now try to connect and set the input and output streams
    connected = super.createConnection(message);

    // Check if Clementine dropped the connection.
    // Is possible when we connect from a public ip and clementine rejects it
    if (connected && !mSocket.isClosed()) {
      // Enter the main loop in the thread
      Message msg = Message.obtain();
      msg.arg1 = CHECK_FOR_DATA_ARG;
      mHandler.sendMessage(msg);

      // Now we are connected
      mLastSong = null;
      mLastState = App.mClementine.getState();

      // Setup the MediaButtonReceiver and the RemoteControlClient
      registerRemoteControlClient();

      // Register MediaButtonReceiver
      IntentFilter filter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
      App.mApp.registerReceiver(mMediaButtonBroadcastReceiver, filter);

      updateNotification();

      // The device shall be awake
      mWakeLock.acquire();

      // We can now reconnect MAX_RECONNECTS times when
      // we get a keep alive timeout
      mLeftReconnects = MAX_RECONNECTS;

      // Set the current time to last keep alive
      setLastKeepAlive(System.currentTimeMillis());

      // Until we get a new connection request from ui,
      // don't request the first data a second time
      mRequestConnect =
          ClementineMessageFactory.buildConnectMessage(
              message.getIp(),
              message.getPort(),
              message.getMessage().getRequestConnect().getAuthCode(),
              false,
              message.getMessage().getRequestConnect().getDownloader());

      // Save started transmitted bytes
      int uid = App.mApp.getApplicationInfo().uid;
      mStartTx = TrafficStats.getUidTxBytes(uid);
      mStartRx = TrafficStats.getUidRxBytes(uid);

      mStartTime = new Date().getTime();
    } else {
      sendUiMessage(new ClementineMessage(ErrorMessage.NO_CONNECTION));
    }

    return connected;
  }
  /** Start the Download */
  private DownloaderResult startDownloading(ClementineMessage clementineMessage) {
    boolean downloadFinished = false;
    DownloaderResult result = new DownloaderResult(DownloadResult.SUCCESSFUL);
    File f = null;
    FileOutputStream fo = null;

    publishProgress(0);

    // Now request the songs
    mClient.sendRequest(clementineMessage);

    while (!downloadFinished) {
      // Check if the user canceled the process
      if (isCancelled()) {
        // Close the stream and delete the incomplete file
        try {
          if (fo != null) {
            fo.flush();
            fo.close();
          }
          if (f != null) {
            f.delete();
          }
        } catch (IOException e) {
        }
        Log.d(TAG, "isCancelled");
        break;
      }

      // Get the raw protocol buffer
      ClementineMessage message = mClient.getProtoc();

      if (message.isErrorMessage()) {
        result = new DownloaderResult(DownloadResult.CONNECTION_ERROR);
        break;
      }

      // Is the download forbidden?
      if (message.getMessageType() == MsgType.DISCONNECT) {
        result = new DownloaderResult(DownloadResult.FOBIDDEN);
        break;
      }

      // Ignore other elements!
      if (message.getMessageType() != MsgType.LIBRARY_CHUNK) {
        continue;
      }

      ResponseLibraryChunk chunk = message.getMessage().getResponseLibraryChunk();

      try {
        // Check if we need to create a new file
        if (f == null) {
          // Check if we have enougth free space
          // size times 2, because we optimise the table later and
          // need space for that too!
          if ((chunk.getSize() * 2) > Utilities.getFreeSpaceExternal()) {
            result = new DownloaderResult(DownloadResult.INSUFFIANT_SPACE);
            break;
          }
          f = mLibrary.getLibraryDb();

          // User wants to override files, so delete it here!
          // The check was already done in processSongOffer()
          if (f.exists()) {
            f.delete();
          }

          f.createNewFile();
          fo = new FileOutputStream(f);
        }

        // Write chunk to sdcard
        fo.write(chunk.getData().toByteArray());

        // Have we downloaded all chunks?
        if (chunk.getChunkCount() == chunk.getChunkNumber()) {
          fo.flush();
          fo.close();
          f = null;
          downloadFinished = true;
        }

        // Update notification
        updateProgress(chunk);
      } catch (IOException e) {
        result = new DownloaderResult(DownloaderResult.DownloadResult.NOT_MOUNTED);
        break;
      }
    }

    // Disconnect at the end
    mClient.disconnect(ClementineMessage.getMessage(MsgType.DISCONNECT));

    // Optimize library table
    if (mLibrary.getLibraryDb().exists()) {
      mLibrary.optimizeTable();
    }

    return result;
  }
  /** Start the Downlaod */
  private DownloaderResult startDownloading(ClementineMessage clementineMessage) {
    boolean downloadFinished = false;
    DownloaderResult result = new DownloaderResult(DownloadResult.SUCCESSFUL);
    File f = null;
    FileOutputStream fo = null;

    // Do we have a playlist?
    mIsPlaylist =
        (clementineMessage.getMessage().getRequestDownloadSongs().getDownloadItem()
            == DownloadItem.APlaylist);
    if (mIsPlaylist) {
      int id = clementineMessage.getMessage().getRequestDownloadSongs().getPlaylistId();
      mPlaylistName = App.mClementine.getPlaylistManager().getPlaylist(id).getName();
    }

    publishProgress(0);

    // Now request the songs
    mClient.sendRequest(clementineMessage);

    while (!downloadFinished) {
      // Check if the user canceled the process
      if (isCancelled()) {
        // Close the stream and delete the incomplete file
        try {
          if (fo != null) {
            fo.flush();
            fo.close();
          }
          if (f != null) {
            f.delete();
          }
        } catch (IOException e) {
        }

        break;
      }

      // Get the raw protocol buffer
      ClementineMessage message = mClient.getProtoc();

      // Check if an error occured
      if (message == null || message.isErrorMessage()) {
        result = new DownloaderResult(DownloadResult.CONNECTION_ERROR);
        break;
      }

      // Is the download forbidden?
      if (message.getMessageType() == MsgType.DISCONNECT) {
        result = new DownloaderResult(DownloadResult.FOBIDDEN);
        break;
      }

      // Download finished?
      if (message.getMessageType() == MsgType.DOWNLOAD_QUEUE_EMPTY) {
        break;
      }

      // Ignore other elements!
      if (message.getMessageType() != MsgType.SONG_FILE_CHUNK) {
        continue;
      }

      ResponseSongFileChunk chunk = message.getMessage().getResponseSongFileChunk();

      // If we received chunk no 0, then we have to decide wether to
      // accept the song offered or not
      if (chunk.getChunkNumber() == 0) {
        processSongOffer(chunk);

        // Update progress here to. If the first (and only) file exists and shall not be
        // overriten, the notification bar shows NULL.
        mCurrentSong = MySong.fromProtocolBuffer(chunk.getSongMetadata());
        publishProgress(mCurrentProgress);
        continue;
      }

      try {
        // Check if we need to create a new file
        if (f == null) {
          // Check if we have enougth free space
          if (chunk.getSize() > Utilities.getFreeSpaceExternal()) {
            result = new DownloaderResult(DownloadResult.INSUFFIANT_SPACE);
            break;
          }

          File dir = new File(BuildDirPath(chunk));
          f = new File(BuildFilePath(chunk));

          // User wants to override files, so delete it here!
          // The check was already done in processSongOffer()
          if (f.exists()) {
            f.delete();
          }

          dir.mkdirs();
          f.createNewFile();
          fo = new FileOutputStream(f);

          // File for download fragment
          mFileUri = Uri.fromFile(f);
        }

        // Write chunk to sdcard
        fo.write(chunk.getData().toByteArray());

        // Have we downloaded all chunks?
        if (chunk.getChunkCount() == chunk.getChunkNumber()) {
          // Index file
          MediaScannerConnection.scanFile(mContext, new String[] {f.getAbsolutePath()}, null, null);
          fo.flush();
          fo.close();
          f = null;
        }

        // Update notification
        updateProgress(chunk);
      } catch (IOException e) {
        result = new DownloaderResult(DownloaderResult.DownloadResult.NOT_MOUNTED);
        break;
      }
    }

    // Disconnect at the end
    mClient.disconnect(ClementineMessage.getMessage(MsgType.DISCONNECT));

    return result;
  }