me.spadival.podmode.PodModeService.java Source code

Java tutorial

Introduction

Here is the source code for me.spadival.podmode.PodModeService.java

Source

/* Copyright (C) 2016  Shailendra Padival
    
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/> */

package me.spadival.podmode;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;

import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

//import com.woodblockwithoutco.remotemetadataprovider.media.RemoteMetadataProvider;
//import com.woodblockwithoutco.remotemetadataprovider.media.listeners.OnMetadataChangeListener;

import me.spadival.podmode.FT311UARTInterface;

import me.spadival.podmode.MusicRetriever.Track;

import me.spadival.podmode.R;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.BitmapFactory;
import android.hardware.usb.UsbManager;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.WifiLock;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.Toast;

public class PodModeService extends Service
        implements Runnable, OnCompletionListener, OnPreparedListener, OnErrorListener, MusicFocusable,
        OnSharedPreferenceChangeListener, PrepareMusicRetrieverTask.MusicRetrieverPreparedListener {

    final static String TAG = "PodModeService";
    final static String PACKAGENAME = "me.spadival.podmode";
    final static String NOTIFYACTION = "me.spadival.podmode.NOTIFY";

    // Message types sent from the BluetoothChatService Handler
    public static final int MESSAGE_STATE_CHANGE = 1;
    public static final int MESSAGE_READ = 2;
    public static final int MESSAGE_WRITE = 3;
    public static final int MESSAGE_DEVICE_NAME = 4;
    public static final int MESSAGE_TOAST = 5;

    // Key names received from the BluetoothChatService Handler
    public static final String DEVICE_NAME = "device_name";
    public static final String TOAST = "toast";

    private volatile Thread mMainThread;

    // The volume we set the media player to when we lose audio focus, but are
    // allowed to reduce
    // the volume instead of stopping playback.
    public static final float DUCK_VOLUME = 0.1f;

    // our media player
    MediaPlayer mPlayer = null;

    // our AudioFocusHelper object, if it's available (it's available on SDK
    // level >= 8)
    // If not available, this will be null. Always check for null before using!
    AudioFocusHelper mAudioFocusHelper = null;

    // indicates the state our service:
    enum State {
        Retrieving, // the MediaRetriever is retrieving music
        Stopped, // media player is stopped and not prepared to play
        Preparing, // media player is preparing...
        Playing, // playback active (media player ready!). (but the media player
        // may actually be
        // paused in this state if we don't have audio focus. But we
        // stay in this state
        // so that we know we have to resume playback once we get
        // focus back)
        Paused // playback paused (media player ready!)
    };

    private State mState = State.Retrieving;

    private int mNowPlaying = 0;
    private int mPrevPlaying = 0;
    private int mPollSpeed = 0;
    private int mChangedCounter = 0;
    private boolean mNotifyHack = false;

    private String mBanner = "";

    // if in Retrieving mode, this flag indicates whether we should start
    // playing immediately
    // when we are ready or not.
    boolean mStartPlayingAfterRetrieve = false;

    // if mStartPlayingAfterRetrieve is true, this variable indicates the URL
    // that we should
    // start playing when we are ready. If null, we should play a random song
    // from the device
    Uri mWhatToPlayAfterRetrieve = null;

    enum PauseReason {
        UserRequest, // paused by user request
        FocusLoss, // paused because of audio focus loss
    };

    // why did we pause? (only relevant if mState == State.Paused)
    PauseReason mPauseReason = PauseReason.UserRequest;

    // do we have audio focus?
    enum AudioFocus {
        NoFocusNoDuck, // we don't have audio focus, and can't duck
        NoFocusCanDuck, // we don't have focus, but can play at a low volume
        // ("ducking")
        Focused // we have full audio focus
    }

    AudioFocus mAudioFocus = AudioFocus.NoFocusNoDuck;

    String mAppName = "";
    // title of the song we are currently playing
    String mSongTitle = "";
    String mAlbumArtUri = null;
    int mElapsedTime = 0;

    // whether the song we are playing is streaming from the network
    boolean mIsStreaming = false;

    // Wifi lock that we hold when streaming files from the internet, in order
    // to prevent the
    // device from shutting off the Wifi radio
    WifiLock mWifiLock;

    // The ID we use for the notification (the onscreen alert that appears at
    // the notification
    // area at the top of the screen as an icon -- and as text as well if the
    // user expands the
    // notification area).
    final int NOTIFICATION_ID = 1;

    // Our instance of our MusicRetriever, which handles scanning for media and
    // providing titles and URIs as we need.
    MusicRetriever mRetriever;

    AudioManager mAudioManager;
    NotificationManager mNotificationManager;

    Notification mNotification = null;

    final byte[] DEVTYPESIZE = { 0x00, 0x13, 0x01, 0x09 };
    final String WAKETAG = "PodModeService";

    enum deviceType {
        FT311D, FT232PL2303, MC05BT
    };

    private deviceType mDeviceType;
    private FTDriver mSerialHost;
    private FT311UARTInterface mSerialDevice;
    private BluetoothAdapter mBluetoothAdapter;
    private BluetoothDevice mBluetoothDevice;
    private BluetoothSerialService mBTDevice = null;

    private int mSerialBaudRate = FTDriver.BAUD57600;
    private int mReadLen = 0;
    private byte[] mReadBuffer = new byte[4096];

    private boolean mPodRunning = false;
    private boolean mLaunchFirstTime = true;

    private WakeLock podWakeLock = null;

    enum podStat {
        WAITING, SIMPLEREMOTE, ADVANCEDREMOTE, ADVANCEDHACK, DISPLAYREMOTE
    };

    enum modeStat {
        Off, Songs, Albums
    }

    private byte mUpdateFlag = 0x00;

    private modeStat mPodRepeatMode = modeStat.Off;
    private modeStat mPodShuffleMode = modeStat.Off;

    private podStat mPodStatus = podStat.WAITING;

    private String mSimpleRemoteApp;
    private String mAdvancedRemoteApp;

    private String mAccessoryName = "-";
    private String mAccessoryMnf = "-";
    private String mAccessoryModel = "-";
    private boolean mHTTPsend = false;

    // Polling Timer Task run during Advanced Remote mode to send back elapsed
    // time and/or any change of song track.

    private void serialWrite(byte[] respBytes) {
        if (mDeviceType == deviceType.FT311D)
            mSerialDevice.SendData(respBytes.length, respBytes);
        else if (mDeviceType == deviceType.FT232PL2303)
            mSerialHost.write(respBytes);
        else if (mDeviceType == deviceType.MC05BT)
            mBTDevice.write(respBytes);
    }

    int[] mLen = new int[1];

    private int mSerialRead(byte[] readBuf) {

        if (mDeviceType == deviceType.FT311D) {
            mSerialDevice.ReadData(readBuf.length, readBuf, mLen);
            return mLen[0];
        } else if (mDeviceType == deviceType.FT232PL2303)
            return mSerialHost.read(readBuf);
        else if (mDeviceType == deviceType.MC05BT)
            return mBTDevice.read(readBuf);
        else
            return 0;

    }

    private Timer mMediaChangeTimer;

    private TimerTask mMediaChangeTask = new TimerTask() {
        public void run() {
            if (mPollSpeed != 0) {
                byte[] respBytes = new byte[] { (byte) 0xFF, 0x55, 0x08, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00 };

                int elapsedTime = 0;

                if (mPlayer != null) {
                    if (mPlayer.isPlaying()) {
                        elapsedTime = mPlayer.getCurrentPosition();
                        if (mState == State.Playing || mState == State.Preparing) {
                            respBytes[6] = 0x04;
                        }
                    }
                } else {
                    respBytes[6] = 0x04;
                    mElapsedTime += 500;
                    elapsedTime = mElapsedTime;
                }

                respBytes[7] = (byte) (elapsedTime >>> 24);
                respBytes[8] = (byte) (elapsedTime >>> 16);
                respBytes[9] = (byte) (elapsedTime >>> 8);
                respBytes[10] = (byte) elapsedTime;

                if (mChangedCounter > 0 && mChangedCounter++ >= mPollSpeed * 2) {
                    mChangedCounter = 0;
                    mUpdateFlag = 0x01;
                    respBytes[6] = 0x01;
                    if (mNowPlaying > 0) {
                        respBytes[7] = (byte) ((mNowPlaying) >>> 24);
                        respBytes[8] = (byte) ((mNowPlaying) >>> 16);
                        respBytes[9] = (byte) ((mNowPlaying) >>> 8);
                        respBytes[10] = (byte) mNowPlaying;
                    }

                }

                short checkSum = 0;

                for (int i = 2; i < 11; i++)
                    checkSum += (short) respBytes[i];

                checkSum = (short) (0x100 - checkSum);
                respBytes[11] = (byte) checkSum;
                serialWrite(respBytes);

            }
        }
    };

    private Thread mHttpThread = new Thread() {
        @Override
        public void run() {

            HttpClient httpclient = new DefaultHttpClient();
            HttpPost httppost = new HttpPost("http://www.spadival.me/podmode/android.php");
            try {
                List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(6);
                nameValuePairs.add(new BasicNameValuePair("accessoryname", mAccessoryName));
                nameValuePairs.add(new BasicNameValuePair("accessorymanf", mAccessoryMnf));
                nameValuePairs.add(new BasicNameValuePair("accessorymodel", mAccessoryModel));
                nameValuePairs.add(new BasicNameValuePair("accessoryspeed", String.valueOf(mSerialBaudRate)));

                if (mPodStatus == podStat.SIMPLEREMOTE)
                    nameValuePairs.add(new BasicNameValuePair("accessorymode", "Simple Remote"));
                if (mPodStatus == podStat.ADVANCEDHACK || mPodStatus == podStat.ADVANCEDREMOTE)
                    nameValuePairs.add(new BasicNameValuePair("accessorymode", "Advanced Remote"));
                else
                    nameValuePairs.add(new BasicNameValuePair("accessorymode", "-"));

                nameValuePairs.add(new BasicNameValuePair("devicename", android.os.Build.MODEL));
                nameValuePairs.add(new BasicNameValuePair("devicemanf", android.os.Build.MANUFACTURER));
                httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
                httpclient.execute(httppost);

                SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(PodModeService.this);

                SharedPreferences.Editor editor = prefs.edit();
                editor.putString("accessoryName", mAccessoryName);
                editor.putString("accessoryMnf", mAccessoryMnf);
                editor.putString("accessoryModel", mAccessoryModel);
                editor.commit();

            } catch (ClientProtocolException e) {
                e.printStackTrace();

            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    };

    private BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)
                    || UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
                mPodRunning = false;
                Toast.makeText(getApplicationContext(), "USB disconnected, Closing PodMode", Toast.LENGTH_LONG)
                        .show();
                stopSelf();
            }
        }
    };

    // The Handler that gets information back from the BluetoothChatService
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MESSAGE_STATE_CHANGE:
                switch (msg.arg1) {
                case BluetoothSerialService.STATE_CONNECTED:
                    if (!mPodRunning) {
                        mDeviceType = deviceType.MC05BT;
                        mPodRunning = true;
                        mReadLen = 0;
                        mBanner = getString(R.string.podmode_connected);
                        localBroadcast(false);
                        setUpAsForeground(getString(R.string.podmode_connected));
                        mainloop();
                    }
                    break;
                case BluetoothSerialService.STATE_CONNECTING:
                    break;
                case BluetoothSerialService.STATE_LISTEN:
                    if (!mPodRunning) {
                        mBanner = getString(R.string.bad_usb);
                        setUpAsForeground(mBanner);
                        localBroadcast(true);
                        stopSelf();
                    }
                    break;
                case BluetoothSerialService.STATE_DISCONNECTED:
                    if (mDeviceType == deviceType.MC05BT) {
                        mPodRunning = false;
                        Toast.makeText(getApplicationContext(), "Bluetooth disconnected, Closing PodMode",
                                Toast.LENGTH_LONG).show();
                        Intent localIntent = new Intent(MainActivity.CLOSE);
                        LocalBroadcastManager.getInstance(PodModeService.this).sendBroadcast(localIntent);
                        stopSelf();
                    }
                    break;
                case BluetoothSerialService.STATE_NONE:
                    break;
                }
                break;
            case MESSAGE_WRITE:
                break;
            case MESSAGE_READ:
                break;
            case MESSAGE_DEVICE_NAME:
                // save the connected device's name
                String mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);
                Toast.makeText(getApplicationContext(), "Connected to " + mConnectedDeviceName, Toast.LENGTH_SHORT)
                        .show();
                break;
            case MESSAGE_TOAST:
                Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST), Toast.LENGTH_SHORT).show();
                break;
            }
        }
    };

    private Timer mNotifyChangeTimer = null;

    private TimerTask mNotifyChangeTask = null;

    private BroadcastReceiver mNotifyReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action.equals(NOTIFYACTION) && mPodStatus == podStat.ADVANCEDHACK) {
                String packageName = intent.getStringExtra("package");
                String appName = intent.getStringExtra("appname");
                String text = intent.getStringExtra("text");
                Log.d("PodMode", "NotifyReceiver " + text);

                if (packageName.equals(mAdvancedRemoteApp))
                    return;

                mRetriever.changeApp(false, appName, text, 999);

                synchronized (this) {

                    mChangedCounter = 1;
                    mNotifyHack = true;

                    if (mNotifyChangeTask != null)
                        mNotifyChangeTask.cancel();

                    mNotifyChangeTask = new TimerTask() {
                        public void run() {
                            mRetriever.changeApp(false, mAppName, mSongTitle, 999);
                            mChangedCounter = 1;
                            mNotifyHack = true;
                        }
                    };

                    mNotifyChangeTimer = new Timer();

                    mNotifyChangeTimer.schedule(mNotifyChangeTask, 15000);

                }

            }
        }
    };

    // private RemoteMetadataProvider mProvider;

    /*
     * public OnMetadataChangeListener mMetadataListner = new
     * OnMetadataChangeListener() {
     * 
     * @Override public void onMetadataChanged(String artist, String title,
     * String album, String albumArtist, long duration) {
     * 
     * if (title == null || album == null) return;
     * 
     * if (title.equals(mSongTitle)) return;
     * 
     * if (mPodStatus == podStat.ADVANCEDHACK) {
     * 
     * String appName = album; String text = title;
     * 
     * mRetriever.changeApp(false, appName, text, duration);
     * 
     * synchronized (this) { mSongTitle = text; mAppName = appName; mElapsedTime
     * = 0;
     * 
     * mChangedCounter = 1; mNotifyHack = true; } } } };
     */

    @Override
    public void onCreate() {

        IntentFilter usbFilter = new IntentFilter();
        usbFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
        usbFilter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
        registerReceiver(mUsbReceiver, usbFilter);
        // mProvider = RemoteMetadataProvider.getInstance(this);

        // mProvider.setOnMetadataChangeListener(mMetadataListner);
        // mProvider.acquireRemoteControls();
        LocalBroadcastManager bManager = LocalBroadcastManager.getInstance(this);
        IntentFilter notifyFilter = new IntentFilter();
        notifyFilter.addAction(NOTIFYACTION);
        bManager.registerReceiver(mNotifyReceiver, notifyFilter);

        mSerialHost = new FTDriver((UsbManager) getSystemService(Context.USB_SERVICE));

        mSerialDevice = new FT311UARTInterface(this, null);

        // Get the local Bluetooth adapter
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

        // Get a set of currently paired devices
        Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();

        mBluetoothDevice = null;

        /*   // If there are paired devices, add each one to the ArrayAdapter
           if (pairedDevices.size() > 0) {
              for (BluetoothDevice device : pairedDevices) {
        if (device.getName().equals("PodModeBT"))
           mBluetoothDevice = device;
              }
           }
            
           if (mBluetoothDevice != null) {
              mBTDevice = new BluetoothSerialService(this, mHandler);
              mBTDevice.connect(mBluetoothDevice);
           } */

        // Create the Wifi lock (this does not acquire the lock, this just
        // creates it)
        mWifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
                .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");

        mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);

        // Create the retriever and start an asynchronous task that will prepare
        // it.

        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);

        mRetriever = new MusicRetriever(getContentResolver(), getApplicationContext(), true, prefs);

        // mRetriever.switchToMainPlaylist();

        prefs.registerOnSharedPreferenceChangeListener((OnSharedPreferenceChangeListener) this);

        mNowPlaying = prefs.getInt("nowplaying", 0);

        String mBaudrate = prefs.getString("baud_rate", "57600");

        if (mBaudrate.equals("57600"))
            mSerialBaudRate = FTDriver.BAUD57600;
        else if (mBaudrate.equals("38400"))
            mSerialBaudRate = FTDriver.BAUD38400;
        else if (mBaudrate.equals("19200"))
            mSerialBaudRate = FTDriver.BAUD19200;

        (new PrepareMusicRetrieverTask(mRetriever, this)).execute();

        // create the Audio Focus Helper, if the Audio Focus feature is
        // available (SDK 8 or above)
        if (android.os.Build.VERSION.SDK_INT >= 8)
            mAudioFocusHelper = new AudioFocusHelper(getApplicationContext(), this);
        else
            mAudioFocus = AudioFocus.Focused; // no focus feature, so we always
        // "have" audio focus

        boolean wakeLockPreferred = prefs.getBoolean("wakelock", false);

        if (podWakeLock == null && wakeLockPreferred) {
            PowerManager powerMgr = (PowerManager) getSystemService(Context.POWER_SERVICE);
            podWakeLock = powerMgr.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, WAKETAG);
            podWakeLock.acquire();
        }

        PackageManager pxm = getPackageManager();
        Intent mediaIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);

        List<ResolveInfo> mAppsInfo = pxm.queryBroadcastReceivers(mediaIntent, 0);

        mSimpleRemoteApp = prefs.getString("selectapp", null);
        mAdvancedRemoteApp = prefs.getString("selectadvancedapp", PACKAGENAME);

        // Make sure App preferences are still valid and Apps haven't been
        // uninstalled.

        if (mAppsInfo.size() > 0) {

            CharSequence[] entryValues = new CharSequence[mAppsInfo.size()];
            CharSequence[] advEntryValues = new CharSequence[mAppsInfo.size() + 1];

            advEntryValues[0] = PACKAGENAME;

            int i = 0;
            for (ResolveInfo info : mAppsInfo) {
                entryValues[i] = (String) info.activityInfo.packageName;
                advEntryValues[i + 1] = (String) info.activityInfo.packageName;

                i++;
            }

            boolean entryFound = false;

            if (mSimpleRemoteApp != null) {
                for (i = 0; i < entryValues.length; i++) {
                    if (mSimpleRemoteApp.equals(entryValues[i])) {
                        entryFound = true;
                    }
                }
            }

            if (!entryFound) {
                SharedPreferences.Editor editor = prefs.edit();
                editor.putString("selectapp", (String) entryValues[0]);
                editor.commit();
                mSimpleRemoteApp = (String) entryValues[0];
            }

            entryFound = false;

            if (mAdvancedRemoteApp != null) {
                for (i = 0; i < advEntryValues.length; i++) {
                    if (mAdvancedRemoteApp.equals(advEntryValues[i])) {
                        entryFound = true;
                    }
                }
            }

            if (!entryFound) {
                SharedPreferences.Editor editor = prefs.edit();
                editor.putString("selectadvancedapp", (String) advEntryValues[0]);
                editor.commit();
                mAdvancedRemoteApp = (String) advEntryValues[0];

            }
        } else {
            SharedPreferences.Editor editor = prefs.edit();
            editor.putString("selectadvancedapp", PACKAGENAME);
            editor.commit();
            mAdvancedRemoteApp = PACKAGENAME;
        }

        mAccessoryName = prefs.getString("accessoryName", null);
        mAccessoryMnf = prefs.getString("accessoryMnf", null);
        mAccessoryModel = prefs.getString("accessoryModel", null);

    }

    @Override
    public void onMusicRetrieverPrepared() {
        // Done retrieving!
        mState = State.Stopped;

        if (mSerialHost.begin(mSerialBaudRate)) {
            //   mBTDevice.stop();
            mDeviceType = deviceType.FT232PL2303;
            mPodRunning = true;
        } else {
            if (mSerialDevice.ResumeAccessory() == 0) {
                //      mBTDevice.stop();
                mDeviceType = deviceType.FT311D;
                mSerialDevice.SetConfig(mSerialBaudRate, (byte) 0x8, (byte) 0x1, (byte) 0x0, (byte) 0x0);
                mPodRunning = true;
            } else if (mBTDevice != null) {
                if (mBTDevice.getState() == BluetoothSerialService.STATE_CONNECTED) {
                    mDeviceType = deviceType.MC05BT;
                    mPodRunning = true;
                } else {
                    mBanner = getString(R.string.podmode_connecting);
                    setUpAsForeground(mBanner);
                    localBroadcast(true);
                }
            } else {
                mBanner = getString(R.string.bad_usb);
                setUpAsForeground(mBanner);
                localBroadcast(true);
                stopSelf();
            }
        }

        if (mPodRunning) {
            mReadLen = 0;
            mBanner = getString(R.string.podmode_connected);
            localBroadcast(false);
            setUpAsForeground(getString(R.string.podmode_connected));

            mainloop();
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        localBroadcast(false);
        return START_STICKY;
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {

        if (key.equals("selectapp")) {
            mSimpleRemoteApp = sharedPreferences.getString("selectapp", null);
        }

        if (key.equals("selectadvancedapp")) {
            mAdvancedRemoteApp = sharedPreferences.getString("selectadvancedapp", null);
        }

        mLaunchFirstTime = true;
    }

    private void mainloop() {
        if (mMainThread == null) {
            mMainThread = new Thread(this);
            mMainThread.start(); // This starts run() below
        }
    }

    public void broadcastMediaButtons(int keyCode, String app) {
        long eventtime = SystemClock.uptimeMillis();

        Intent downIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
        KeyEvent downEvent = new KeyEvent(eventtime, eventtime, KeyEvent.ACTION_DOWN, keyCode, 0);
        downIntent.putExtra(Intent.EXTRA_KEY_EVENT, downEvent);
        if (app != null)
            downIntent.setPackage(app);

        sendOrderedBroadcast(downIntent, null);

        eventtime = SystemClock.uptimeMillis();

        Intent upIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
        KeyEvent upEvent = new KeyEvent(eventtime, eventtime, KeyEvent.ACTION_UP, keyCode, 0);
        upIntent.putExtra(Intent.EXTRA_KEY_EVENT, upEvent);
        if (app != null)
            upIntent.setPackage(app);

        sendOrderedBroadcast(upIntent, null);

    }

    public void run() {
        podCommand pCommand = null;
        podResponse pResponse = null;
        byte[] respBytes = null;

        long eventtime;
        Intent downIntent = null;
        KeyEvent downEvent = null;
        Intent upIntent = null;
        KeyEvent upEvent = null;

        // serialWrite(new byte[] { (byte) 0xFF, 0x55, 0x02, 0x00, 0x00,
        // (byte) 0xFE });

        while (mPodRunning) {
            pCommand = readCommand();
            if (pCommand == null)
                continue;

            if (pCommand.mode == 0x03)
                mPodStatus = podStat.DISPLAYREMOTE;

            if (mAccessoryName != null
                    && (mPodStatus == podStat.SIMPLEREMOTE || mPodStatus == podStat.DISPLAYREMOTE
                            || mPodStatus == podStat.ADVANCEDREMOTE || mPodStatus == podStat.ADVANCEDHACK)
                    && mHTTPsend) {
                mHTTPsend = false;
                mHttpThread.start();
            }

            if (mLaunchFirstTime
                    && (pCommand.mode == 02 || (pCommand.mode == 04 && !mAdvancedRemoteApp.equals(PACKAGENAME)))) {

                String launchApp = null;

                if (pCommand.mode == 02) {
                    mBanner = getString(R.string.simple_remote);
                    launchApp = mSimpleRemoteApp;
                }

                if (pCommand.mode == 04) {
                    mPodStatus = podStat.ADVANCEDREMOTE;
                    if (!mAdvancedRemoteApp.equals(PACKAGENAME))
                        mPodStatus = podStat.ADVANCEDHACK;

                    mBanner = getString(R.string.advanced_remote);
                    launchApp = mAdvancedRemoteApp;
                }

                if (launchApp != null) {
                    PackageManager pm = getPackageManager();
                    Intent LaunchIntent = pm.getLaunchIntentForPackage(launchApp);

                    startActivity(LaunchIntent);

                    ResolveInfo info = pm.resolveActivity(LaunchIntent, PackageManager.MATCH_DEFAULT_ONLY);
                    mSongTitle = (String) info.loadLabel(pm);
                    mElapsedTime = 0;

                    if (pCommand.mode == 04)
                        mRetriever.changeApp(false, mSongTitle, getString(R.string.advanced_remote), 999);

                    mAlbumArtUri = "android.resource://" + launchApp + "/" + String.valueOf(info.getIconResource());
                    setUpAsForeground(mBanner);
                    localBroadcast(false);
                }

                mLaunchFirstTime = false;
            }

            if (pCommand.mode == 02) {

                mPodStatus = podStat.SIMPLEREMOTE;

                switch (pCommand.command) {

                case RemoteRelease:
                    respBytes = new byte[] { (byte) 0x01, 0x00, 0x00 };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case RemotePlayPause:

                    eventtime = SystemClock.uptimeMillis();

                    downIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
                    downEvent = new KeyEvent(eventtime, eventtime, KeyEvent.ACTION_DOWN,
                            KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, 0);
                    downIntent.putExtra(Intent.EXTRA_KEY_EVENT, downEvent);
                    if (mSimpleRemoteApp != null)
                        downIntent.setPackage(mSimpleRemoteApp);

                    sendOrderedBroadcast(downIntent, null);

                    break;

                case RemoteJustPlay:

                    eventtime = SystemClock.uptimeMillis();

                    downIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
                    downEvent = new KeyEvent(eventtime, eventtime, KeyEvent.ACTION_DOWN,
                            KeyEvent.KEYCODE_MEDIA_PLAY, 0);
                    downIntent.putExtra(Intent.EXTRA_KEY_EVENT, downEvent);
                    if (mSimpleRemoteApp != null)
                        downIntent.setPackage(mSimpleRemoteApp);

                    sendOrderedBroadcast(downIntent, null);

                    break;

                case RemoteJustPause:

                    eventtime = SystemClock.uptimeMillis();

                    downIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
                    downEvent = new KeyEvent(eventtime, eventtime, KeyEvent.ACTION_DOWN,
                            KeyEvent.KEYCODE_MEDIA_PAUSE, 0);
                    downIntent.putExtra(Intent.EXTRA_KEY_EVENT, downEvent);
                    if (mSimpleRemoteApp != null)
                        downIntent.setPackage(mSimpleRemoteApp);

                    sendOrderedBroadcast(downIntent, null);

                    break;

                case RemoteSkipFwd:

                    eventtime = SystemClock.uptimeMillis();

                    if (downEvent != null) {
                        if (downEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_NEXT
                                || downEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD) {
                            if ((eventtime - downEvent.getEventTime()) > 1000) {

                                downIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
                                downEvent = new KeyEvent(eventtime, eventtime, KeyEvent.ACTION_DOWN,
                                        KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, 0);
                                downIntent.putExtra(Intent.EXTRA_KEY_EVENT, downEvent);
                                if (mSimpleRemoteApp != null)
                                    downIntent.setPackage(mSimpleRemoteApp);

                                sendOrderedBroadcast(downIntent, null);

                                upIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
                                upEvent = new KeyEvent(eventtime, eventtime, KeyEvent.ACTION_UP,
                                        KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, 0);
                                upIntent.putExtra(Intent.EXTRA_KEY_EVENT, upEvent);
                                if (mSimpleRemoteApp != null)
                                    upIntent.setPackage(mSimpleRemoteApp);

                                sendOrderedBroadcast(upIntent, null);
                            }

                        }

                    } else {

                        downIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
                        downEvent = new KeyEvent(eventtime, eventtime, KeyEvent.ACTION_DOWN,
                                KeyEvent.KEYCODE_MEDIA_NEXT, 0);
                        downIntent.putExtra(Intent.EXTRA_KEY_EVENT, downEvent);
                        if (mSimpleRemoteApp != null)
                            downIntent.setPackage(mSimpleRemoteApp);
                    }

                    break;
                case RemoteSkipRwd:

                    eventtime = SystemClock.uptimeMillis();

                    if (downEvent != null) {
                        if (downEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PREVIOUS
                                || downEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_REWIND) {
                            if ((eventtime - downEvent.getEventTime()) > 1000) {

                                downIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
                                downEvent = new KeyEvent(eventtime, eventtime, KeyEvent.ACTION_DOWN,
                                        KeyEvent.KEYCODE_MEDIA_REWIND, 0);
                                downIntent.putExtra(Intent.EXTRA_KEY_EVENT, downEvent);
                                if (mSimpleRemoteApp != null)
                                    downIntent.setPackage(mSimpleRemoteApp);
                                sendOrderedBroadcast(downIntent, null);

                                upIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
                                upEvent = new KeyEvent(eventtime, eventtime, KeyEvent.ACTION_UP,
                                        KeyEvent.KEYCODE_MEDIA_REWIND, 0);
                                upIntent.putExtra(Intent.EXTRA_KEY_EVENT, upEvent);
                                if (mSimpleRemoteApp != null)
                                    upIntent.setPackage(mSimpleRemoteApp);
                                sendOrderedBroadcast(upIntent, null);
                            }

                        }

                    } else {

                        downIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
                        downEvent = new KeyEvent(eventtime, eventtime, KeyEvent.ACTION_DOWN,
                                KeyEvent.KEYCODE_MEDIA_PREVIOUS, 0);
                        downIntent.putExtra(Intent.EXTRA_KEY_EVENT, downEvent);
                        if (mSimpleRemoteApp != null)
                            downIntent.setPackage(mSimpleRemoteApp);
                    }

                    break;
                case RemoteStop:
                    eventtime = SystemClock.uptimeMillis();

                    downIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
                    downEvent = new KeyEvent(eventtime, eventtime, KeyEvent.ACTION_DOWN,
                            KeyEvent.KEYCODE_MEDIA_STOP, 0);
                    downIntent.putExtra(Intent.EXTRA_KEY_EVENT, downEvent);
                    if (mSimpleRemoteApp != null)
                        downIntent.setPackage(mSimpleRemoteApp);
                    sendOrderedBroadcast(downIntent, null);

                    break;

                case RemoteButtonRel:

                    eventtime = SystemClock.uptimeMillis();

                    if (downIntent != null) {
                        if (downEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_NEXT
                                || downEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PREVIOUS)
                            sendOrderedBroadcast(downIntent, null);

                        if (downEvent.getKeyCode() != KeyEvent.KEYCODE_MEDIA_FAST_FORWARD
                                && downEvent.getKeyCode() != KeyEvent.KEYCODE_MEDIA_REWIND) {
                            upIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
                            upEvent = new KeyEvent(downEvent.getDownTime(), eventtime, KeyEvent.ACTION_UP,
                                    downEvent.getKeyCode(), 0);
                            upIntent.putExtra(Intent.EXTRA_KEY_EVENT, upEvent);
                            if (mSimpleRemoteApp != null)
                                upIntent.setPackage(mSimpleRemoteApp);
                            sendOrderedBroadcast(upIntent, null);
                        }

                    }
                    downIntent = null;
                    downEvent = null;
                    upIntent = null;
                    upEvent = null;

                    break;
                default:
                    break;

                }

            } else {

                switch (pCommand.command) {

                case AppCmd:
                    byte[] appNameBytes = new byte[pCommand.params.length - 3];

                    System.arraycopy(pCommand.params, 2, appNameBytes, 0, appNameBytes.length);

                    String appName = "";

                    try {
                        appName = new String(appNameBytes, "UTF8");
                        Log.d("PodMode", "AppCmd " + appName);
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }

                    break;

                case AppAck:
                    break;

                case GetUpdateFlag:
                    respBytes = new byte[] { 0x00, 0x0A, mUpdateFlag };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;
                case SetUpdateFlag:
                    mUpdateFlag = pCommand.params[0];
                    respBytes = new byte[] { 0x00, 0x01 };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;
                case SwitchToMainPlaylist:
                    if (mPodStatus == podStat.ADVANCEDHACK) {
                        mNowPlaying = 0;
                        mPrevPlaying = 0;
                    } else
                        mNowPlaying = 0;

                    mRetriever.switchToMainPlaylist();
                    respBytes = new byte[] { 0x00, 0x01 };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;
                case SwitchToItem:

                    int itemNo = 0;

                    itemNo = pCommand.params[4] & 0xFF;
                    itemNo += ((pCommand.params[3] & 0xFF) << 8);
                    itemNo += ((pCommand.params[2] & 0xFF) << 16);
                    itemNo += ((pCommand.params[1] & 0xFF) << 24);

                    if ((mPodStatus == podStat.ADVANCEDHACK && mNotifyHack)) {
                        mNotifyHack = false;
                    } else {
                        if (mRetriever.switchToItem((int) pCommand.params[0], itemNo)) {
                            if (pCommand.params[0] == (byte) 0x05) {
                                mNowPlaying = itemNo;
                                tryToGetAudioFocus();
                                playNextSong(null);
                            }
                        }
                    }

                    respBytes = new byte[] { 0x00, 0x01 };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case GetCountForType:
                    respBytes = new byte[] { 0x00, 0x19, 0x00, 0x00, 0x00, 0x00 };
                    int num = mRetriever.getCountForType((int) pCommand.params[0]);

                    respBytes[5] = (byte) (num & 0xFF);
                    respBytes[4] = (byte) ((num >> 8) & 0xFF);
                    respBytes[3] = (byte) ((num >> 16) & 0xFF);
                    respBytes[2] = (byte) ((num >> 24) & 0xFF);

                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case GetItemNames:
                    int startPos = 0;
                    int count = 0;

                    startPos = pCommand.params[4] & 0xFF;
                    startPos += ((pCommand.params[3] & 0xFF) << 8);
                    startPos += ((pCommand.params[2] & 0xFF) << 16);
                    startPos += ((pCommand.params[1] & 0xFF) << 24);

                    count = pCommand.params[8] & 0xFF;
                    count += ((pCommand.params[7] & 0xFF) << 8);
                    count += ((pCommand.params[6] & 0xFF) << 16);
                    count += ((pCommand.params[5] & 0xFF) << 24);

                    String[] itemNames = mRetriever.GetItemNames((int) pCommand.params[0], startPos, count);

                    if (itemNames != null) {
                        for (int i = 0; i < itemNames.length; i++) {
                            byte[] part1 = { (byte) 0x00, (byte) 0x1B, (byte) (startPos >>> 24),
                                    (byte) (startPos >>> 16), (byte) (startPos >>> 8), (byte) startPos };

                            startPos++;

                            respBytes = new String(new String(part1) + itemNames[i] + '\0').getBytes();
                            pResponse = new podResponse(pCommand, respBytes);
                            serialWrite(pResponse.getBytes());
                        }
                    }
                    break;

                case GetTimeStatus:
                    respBytes = new byte[] { 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

                    if (mState != State.Preparing && mState != State.Retrieving) {

                        int trackLen = 0;

                        if (mPlayer != null)
                            trackLen = mPlayer.getDuration();
                        respBytes[2] = (byte) (trackLen >>> 24);
                        respBytes[3] = (byte) (trackLen >>> 16);
                        respBytes[4] = (byte) (trackLen >>> 8);
                        respBytes[5] = (byte) trackLen;

                        int elapsedTime = 0;
                        if (mPlayer != null)
                            elapsedTime = mPlayer.getCurrentPosition();

                        respBytes[6] = (byte) (elapsedTime >>> 24);
                        respBytes[7] = (byte) (elapsedTime >>> 16);
                        respBytes[8] = (byte) (elapsedTime >>> 8);
                        respBytes[9] = (byte) elapsedTime;

                        switch (mState) {
                        case Stopped:
                            respBytes[10] = (byte) 0x00;
                            break;
                        case Playing:
                            respBytes[10] = (byte) 0x01;
                            break;
                        case Paused:
                            respBytes[10] = (byte) 0x02;
                            break;
                        case Preparing:
                            respBytes[10] = (byte) 0x01;
                            break;
                        case Retrieving:
                            respBytes[10] = (byte) 0x01;
                            break;
                        }
                    }

                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case GetPlaylistPos:
                    respBytes = new byte[] { 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00 };

                    respBytes[2] = (byte) ((mNowPlaying) >>> 24);
                    respBytes[3] = (byte) ((mNowPlaying) >>> 16);
                    respBytes[4] = (byte) ((mNowPlaying) >>> 8);
                    respBytes[5] = (byte) mNowPlaying;

                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case GetSongTitle:
                    byte[] part1 = new byte[] { 0x00, 0x21 };
                    int index;
                    index = pCommand.params[3] & 0xFF;
                    index += ((pCommand.params[2] & 0xFF) << 8);
                    index += ((pCommand.params[1] & 0xFF) << 16);
                    index += ((pCommand.params[0] & 0xFF) << 24);

                    if (index == -1)
                        index = 0;

                    respBytes = new String(new String(part1) + mRetriever.getTrack(index).title + '\0').getBytes();

                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case GetSongArtist:
                    part1 = new byte[] { 0x00, 0x23 };
                    index = pCommand.params[3] & 0xFF;
                    index += ((pCommand.params[2] & 0xFF) << 8);
                    index += ((pCommand.params[1] & 0xFF) << 16);
                    index += ((pCommand.params[0] & 0xFF) << 24);

                    if (index == -1)
                        index = 0;

                    respBytes = new String(new String(part1) + mRetriever.getTrack(index).artist + '\0').getBytes();

                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case GetSongAlbum:
                    part1 = new byte[] { 0x00, 0x25 };
                    index = pCommand.params[3] & 0xFF;
                    index += ((pCommand.params[2] & 0xFF) << 8);
                    index += ((pCommand.params[1] & 0xFF) << 16);
                    index += ((pCommand.params[0] & 0xFF) << 24);

                    if (index == -1)
                        index = 0;

                    respBytes = new String(new String(part1) + mRetriever.getTrack(index).album + '\0').getBytes();

                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case PollingMode:
                    respBytes = new byte[] { 0x00, 0x01 };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());

                    mPollSpeed = (byte) pCommand.params[0];
                    if (pCommand.params[0] == (byte) 0x01 && mUpdateFlag != (byte) 0x01) {
                        mUpdateFlag = pCommand.params[0];

                        if (mMediaChangeTimer == null)
                            mMediaChangeTimer = new Timer();

                        mMediaChangeTimer.scheduleAtFixedRate(mMediaChangeTask, 0, 500);

                    } else if (pCommand.params[0] == (byte) 0x00 && mUpdateFlag != (byte) 0x00) {

                        mUpdateFlag = pCommand.params[0];
                        if (mMediaChangeTimer != null)
                            mMediaChangeTimer.cancel();
                    }

                    break;

                case ExecPlaylist:
                    itemNo = pCommand.params[3] & 0xFF;
                    itemNo += ((pCommand.params[2] & 0xFF) << 8);
                    itemNo += ((pCommand.params[1] & 0xFF) << 16);
                    itemNo += ((pCommand.params[0] & 0xFF) << 24);

                    if (itemNo == -1)
                        itemNo = 0;

                    mRetriever.ExecPlaylist();

                    if (mPodShuffleMode == modeStat.Songs)
                        mRetriever.shuffleTracks();

                    mNowPlaying = itemNo;
                    tryToGetAudioFocus();
                    playNextSong(null);

                    respBytes = new byte[] { 0x00, 0x01 };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case PlaybackControl:
                    switch (pCommand.params[0]) {
                    case 0x01:
                        // processStopRequest();
                        processTogglePlaybackRequest();
                        break;
                    case 0x02:
                        processStopRequest();
                        break;
                    case 0x03:
                        processPauseRequest();
                        processSkipRequest();
                        break;
                    case 0x04:
                        processPauseRequest();
                        processSkipRwdRequest();
                        break;
                    case 0x05:
                        // processSkipRequest();
                        break;
                    case 0x06:
                        // processRewindRequest();
                        break;
                    case 0x07:
                        // TODO Add Stop FF/RR function
                        break;
                    }

                    respBytes = new byte[] { 0x00, 0x01 };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case GetPlayListSongNum:
                    respBytes = new byte[] { 0x00, 0x36, 0x00, 0x00, 0x00, 0x00 };
                    num = mRetriever.getCount();

                    respBytes[5] = (byte) (num & 0xFF);
                    respBytes[4] = (byte) ((num >> 8) & 0xFF);
                    respBytes[3] = (byte) ((num >> 16) & 0xFF);
                    respBytes[2] = (byte) ((num >> 24) & 0xFF);

                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case JumpToSong:
                    itemNo = pCommand.params[3] & 0xFF;
                    itemNo += ((pCommand.params[2] & 0xFF) << 8);
                    itemNo += ((pCommand.params[1] & 0xFF) << 16);
                    itemNo += ((pCommand.params[0] & 0xFF) << 24);

                    mNowPlaying = itemNo;
                    tryToGetAudioFocus();
                    playNextSong(null);

                    respBytes = new byte[] { 0x00, 0x01 };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case FrankPlaylist:
                    respBytes = new byte[] { 0x00, 0x4F, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF };
                    num = mRetriever.getPlaylistNum();

                    if (num != -1) {
                        respBytes[5] = (byte) (num & 0xFF);
                        respBytes[4] = (byte) ((num >> 8) & 0xFF);
                        respBytes[3] = (byte) ((num >> 16) & 0xFF);
                        respBytes[2] = (byte) ((num >> 24) & 0xFF);
                    }

                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case StartID:
                    respBytes = new byte[] { 0x02 };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case GetPodProtocols:
                    // start
                    respBytes = new byte[] { 0x02 };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    if (pCommand.rawBytes[13] == (byte) 0x00 && pCommand.rawBytes[14] == (byte) 0x00
                            && pCommand.rawBytes[15] == (byte) 0x00 && pCommand.rawBytes[16] == (byte) 0x00) {
                        // respBytes = new byte[] { 0x00 };
                        // pResponse = new podResponse(pCommand, respBytes);
                        // serialWrite(pResponse.getBytes());

                    } else {
                        respBytes = new byte[] { 0x14 };
                        pResponse = new podResponse(pCommand, respBytes);
                        serialWrite(pResponse.getBytes());
                    }

                    break;

                case DeviceAuthInfo:
                    if (pCommand.length == 4) {
                        respBytes = new byte[] { 0x16 };
                        pResponse = new podResponse(pCommand, respBytes);
                        serialWrite(pResponse.getBytes());

                        respBytes = new byte[] { 0x17, 0x01 };
                        pResponse = new podResponse(pCommand, respBytes);
                        serialWrite(pResponse.getBytes());
                    } else {

                        if (pCommand.rawBytes[7] != pCommand.rawBytes[8]) {
                            respBytes = new byte[] { 0x02 };
                            pResponse = new podResponse(pCommand, respBytes);
                            serialWrite(pResponse.getBytes());

                        } else {
                            respBytes = new byte[] { 0x16 };
                            pResponse = new podResponse(pCommand, respBytes);
                            serialWrite(pResponse.getBytes());

                            respBytes = new byte[] { 0x17, 0x02 };
                            pResponse = new podResponse(pCommand, respBytes);
                            serialWrite(pResponse.getBytes());
                        }
                    }

                    break;

                case DeviceAuthSig:
                    respBytes = new byte[] { 0x19 };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case GetPodOptions:
                    // start
                    if (pCommand.rawBytes[5] == 0x00)
                        respBytes = new byte[] { 0x4C };
                    else
                        respBytes = new byte[] { (byte) 0x02, 0x04 };

                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case GetPodOption:
                    // start
                    respBytes = new byte[] { 0x25 };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case SetIdTokens:
                    respBytes = new byte[] { 0x3A };
                    pResponse = new podResponse(pCommand, respBytes);
                    if (mAccessoryName == null) {
                        mAccessoryName = pResponse.accessoryName;
                        mAccessoryMnf = pResponse.accessoryMnf;
                        mAccessoryModel = pResponse.accessoryModel;
                        mHTTPsend = true;
                    }
                    serialWrite(pResponse.getBytes());
                    break;

                case EndID:
                    respBytes = new byte[] { 0x3C };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());

                    respBytes = new byte[] { 0x14 };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case GetProtoVersion:
                    respBytes = new byte[] { 0x0F };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case DeviceDetails:
                    respBytes = new byte[] { 0x02 };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case DevName:
                    respBytes = new byte[] { 0x00, 0x15, 0x50, 0x6F, 0x64, 0x4D, 0x6F, 0x64, 0x65, 0x00 };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case DevTypeSize:
                    pResponse = new podResponse(pCommand, DEVTYPESIZE);
                    serialWrite(pResponse.getBytes());
                    break;

                case StateInfo:
                    respBytes = new byte[] { 0x0D };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case RemoteNotify:
                    respBytes = new byte[] { 0x02 };
                    pResponse = new podResponse(pCommand, respBytes);

                    serialWrite(pResponse.getBytes());
                    break;

                case SwitchRemote:
                    mPodStatus = podStat.SIMPLEREMOTE;
                    break;

                case ReqAdvRemote:
                    respBytes = new byte[] { 0x04, 0x00 };

                    if (mPodStatus == podStat.ADVANCEDREMOTE || mPodStatus == podStat.ADVANCEDHACK)
                        respBytes[1] = 0x04;

                    pResponse = new podResponse(pCommand, respBytes);

                    serialWrite(pResponse.getBytes());
                    break;

                case StartAdvRemote:
                    mPodStatus = podStat.ADVANCEDREMOTE;
                    if (!mAdvancedRemoteApp.equals(PACKAGENAME))
                        mPodStatus = podStat.ADVANCEDHACK;

                    respBytes = new byte[] { 0x2 };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case EndAdvRemote:
                    mPodStatus = podStat.WAITING;
                    respBytes = new byte[] { 0x2 };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case GetSoftVersion:
                    respBytes = new byte[] { 0x0A };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case GetSerialNum:
                    respBytes = new byte[] { 0x0C };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case DevModel:
                    respBytes = new byte[] { 0x0E };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case SwitchAdvanced:
                    mPodStatus = podStat.ADVANCEDREMOTE;
                    if (!mAdvancedRemoteApp.equals(PACKAGENAME))
                        mPodStatus = podStat.ADVANCEDHACK;
                    break;

                case SetRepeatMode:
                    if (pCommand.params[0] == (byte) 0x00)
                        mPodRepeatMode = modeStat.Off;
                    else if (pCommand.params[0] == (byte) 0x01)
                        mPodRepeatMode = modeStat.Songs;
                    else
                        mPodRepeatMode = modeStat.Albums;
                    respBytes = new byte[] { 0x00, 0x01 };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case GetRepeatMode:
                    respBytes = new byte[] { 0x00, 0x30, 0x00 };
                    if (mPodRepeatMode == modeStat.Songs)
                        respBytes[2] = 0x01;
                    if (mPodRepeatMode == modeStat.Albums)
                        respBytes[2] = 0x02;
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case SetShuffleMode:
                    if (pCommand.params[0] == (byte) 0x00)
                        mPodShuffleMode = modeStat.Off;
                    else if (pCommand.params[0] == (byte) 0x01)
                        mPodShuffleMode = modeStat.Songs;
                    else
                        mPodShuffleMode = modeStat.Albums;

                    respBytes = new byte[] { 0x00, 0x01 };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case GetShuffleMode:
                    respBytes = new byte[] { 0x00, 0x2D, 0x00 };
                    if (mPodShuffleMode == modeStat.Songs)
                        respBytes[2] = 0x01;
                    if (mPodShuffleMode == modeStat.Albums)
                        respBytes[2] = 0x02;
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                case GetScreenSize:
                    respBytes = new byte[] { 0x00, 0x34 };
                    pResponse = new podResponse(pCommand, respBytes);
                    serialWrite(pResponse.getBytes());
                    break;

                default:
                    break;
                }

            }
        }
    }

    private podCommand readCommand() {
        byte[] rbuf = new byte[4096];
        int len = 0;
        boolean notEnoughData = false;

        while (mPodRunning) {

            if (Thread.interrupted())
                return null;

            if (mReadLen < 6 || notEnoughData) {

                try {
                    len = mSerialRead(rbuf);
                } catch (NullPointerException e) {
                    Log.d("PodMode", "Read failed - Null pointer");
                    mPodRunning = false;
                }

                if (len > 0) {
                    System.arraycopy(rbuf, 0, mReadBuffer, mReadLen, len);
                    mReadLen += len;
                    notEnoughData = false;
                } else if (len < 0) {
                    Log.d("PodMode read error : ", String.valueOf(len));

                    if (mDeviceType == deviceType.FT232PL2303) {
                        mSerialHost = new FTDriver((UsbManager) getSystemService(Context.USB_SERVICE));
                        mSerialHost.begin(mSerialBaudRate);
                    }
                }

            } else {

                if (mReadBuffer[0] == (byte) 0xFF && mReadBuffer[1] == (byte) 0x55
                        && mReadBuffer[2] == (byte) 0xF7) {
                    System.arraycopy(mReadBuffer, 3, mReadBuffer, 0, mReadLen - 3);
                    mReadLen -= 3;
                }

                if (mReadBuffer[0] == (byte) 0xFF && mReadBuffer[1] == (byte) 0x55) {

                    int readBufLen = (int) (mReadBuffer[2] & 0xFF);
                    int cmdLen = readBufLen + 4;

                    if (mReadLen >= cmdLen) {

                        podCommand cmd = new podCommand(mReadBuffer, cmdLen);

                        if (mReadLen == cmdLen)
                            mReadLen = 0;
                        else {
                            System.arraycopy(mReadBuffer, cmdLen, mReadBuffer, 0, mReadLen - cmdLen);
                            mReadLen -= cmdLen;
                        }

                        return cmd;

                    } else
                        notEnoughData = true;

                } else {

                    if (mReadLen == 1) {
                        if (mReadBuffer[0] != (byte) 0xFF) {
                            mReadLen = 0;
                        }
                    } else {
                        System.arraycopy(mReadBuffer, 1, mReadBuffer, 0, mReadLen - 1);
                        mReadLen -= 1;
                    }
                }

            }
        }
        return null;
    }

    void processTogglePlaybackRequest() {
        if (mPodStatus == podStat.ADVANCEDHACK) {
            broadcastMediaButtons(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, mAdvancedRemoteApp);
            return;
        }

        if (mState == State.Paused || mState == State.Stopped) {
            processPlayRequest();
        } else {
            processPauseRequest();
        }
    }

    void processPlayRequest() {
        if (mState == State.Retrieving) {
            // If we are still retrieving media, just set the flag to start
            // playing when we're
            // ready
            mWhatToPlayAfterRetrieve = null; // play a random song
            mStartPlayingAfterRetrieve = true;
            return;
        }

        tryToGetAudioFocus();

        // actually play the song

        if (mState == State.Stopped) {
            // If we're stopped, just go ahead to the next song and start
            // playing
            playNextSong(null);
        } else if (mState == State.Paused) {
            // If we're paused, just continue playback and restore the
            // 'foreground service' state.
            mState = State.Playing;
            setUpAsForeground(mSongTitle + getString(R.string.notify_playing));
            configAndStartMediaPlayer();
        }

        // Tell any remote controls that our playback state is 'playing'.

    }

    void processPauseRequest() {
        if (mPodStatus == podStat.ADVANCEDHACK) {
            broadcastMediaButtons(KeyEvent.KEYCODE_MEDIA_PAUSE, mAdvancedRemoteApp);
            return;
        }

        if (mState == State.Retrieving) {
            // If we are still retrieving media, clear the flag that indicates
            // we should start
            // playing when we're ready
            mStartPlayingAfterRetrieve = false;
            return;
        }

        if (mState == State.Playing) {
            // Pause media player and cancel the 'foreground service' state.
            mState = State.Paused;
            mPlayer.pause();
            relaxResources(false); // while paused, we always retain the
            // MediaPlayer
            // do not give up audio focus
        }

    }

    void processRewindRequest() {
        if (mPodStatus == podStat.ADVANCEDHACK) {
            broadcastMediaButtons(KeyEvent.KEYCODE_MEDIA_REWIND, mAdvancedRemoteApp);
            return;
        }

        if (mState == State.Playing || mState == State.Paused)
            mPlayer.seekTo(0);
    }

    void processSkipRequest() {
        if (mPodStatus == podStat.ADVANCEDHACK) {
            mNowPlaying++;
            broadcastMediaButtons(KeyEvent.KEYCODE_MEDIA_NEXT, mAdvancedRemoteApp);
            return;
        }

        if (mState == State.Playing || mState == State.Paused) {
            mNowPlaying++;
            tryToGetAudioFocus();
            playNextSong(null);
        }
    }

    void processSkipRwdRequest() {
        if (mPodStatus == podStat.ADVANCEDHACK) {
            mNowPlaying--;
            broadcastMediaButtons(KeyEvent.KEYCODE_MEDIA_PREVIOUS, mAdvancedRemoteApp);
            return;
        }

        if (mState == State.Playing || mState == State.Paused) {
            mNowPlaying--;
            if (mNowPlaying < 0)
                mNowPlaying = -1;
            tryToGetAudioFocus();
            playNextSong(null);
        }
    }

    void processStopRequest() {
        if (mPodStatus == podStat.ADVANCEDHACK) {
            broadcastMediaButtons(KeyEvent.KEYCODE_MEDIA_STOP, mAdvancedRemoteApp);
            return;
        }

        processStopRequest(false);
    }

    void processStopRequest(boolean force) {
        if (mState == State.Playing || mState == State.Paused || force) {
            mState = State.Stopped;

            // let go of all resources...
            relaxResources(true);
            giveUpAudioFocus();

        }
    }

    /**
     * Makes sure the media player exists and has been reset. This will create
     * the media player if needed, or reset the existing media player if one
     * already exists.
     */
    void createMediaPlayerIfNeeded() {
        if (mPlayer == null) {
            mPlayer = new MediaPlayer();

            // Make sure the media player will acquire a wake-lock while
            // playing. If we don't do
            // that, the CPU might go to sleep while the song is playing,
            // causing playback to stop.
            //
            // Remember that to use this, we have to declare the
            // android.permission.WAKE_LOCK
            // permission in AndroidManifest.xml.
            mPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);

            // we want the media player to notify us when it's ready preparing,
            // and when it's done
            // playing:
            mPlayer.setOnPreparedListener(this);
            mPlayer.setOnCompletionListener(this);
            mPlayer.setOnErrorListener(this);
        } else
            mPlayer.reset();
    }

    /**
     * Releases resources used by the service for playback. This includes the
     * "foreground service" status and notification, the wake locks and possibly
     * the MediaPlayer.
     * 
     * @param releaseMediaPlayer
     *            Indicates whether the Media Player should also be released or
     *            not
     */
    void relaxResources(boolean releaseMediaPlayer) {
        // stop being a foreground service
        stopForeground(true);

        // stop and release the Media Player, if it's available
        if (releaseMediaPlayer && mPlayer != null) {
            mPlayer.reset();
            mPlayer.release();
            mPlayer = null;
        }

        // we can also release the Wifi lock, if we're holding it
        if (mWifiLock.isHeld())
            mWifiLock.release();
    }

    void giveUpAudioFocus() {
        if (mAudioFocus == AudioFocus.Focused && mAudioFocusHelper != null && mAudioFocusHelper.abandonFocus())
            mAudioFocus = AudioFocus.NoFocusNoDuck;
    }

    /**
     * Reconfigures MediaPlayer according to audio focus settings and
     * starts/restarts it. This method starts/restarts the MediaPlayer
     * respecting the current audio focus state. So if we have focus, it will
     * play normally; if we don't have focus, it will either leave the
     * MediaPlayer paused or set it to a low volume, depending on what is
     * allowed by the current focus settings. This method assumes mPlayer !=
     * null, so if you are calling it, you have to do so from a context where
     * you are sure this is the case.
     */
    void configAndStartMediaPlayer() {
        if (mAudioFocus == AudioFocus.NoFocusNoDuck) {
            // If we don't have audio focus and can't duck, we have to pause,
            // even if mState
            // is State.Playing. But we stay in the Playing state so that we
            // know we have to resume
            // playback once we get the focus back.
            if (mPlayer.isPlaying())
                mPlayer.pause();
            return;
        } else if (mAudioFocus == AudioFocus.NoFocusCanDuck)
            mPlayer.setVolume(DUCK_VOLUME, DUCK_VOLUME); // we'll be relatively
        // quiet
        else
            mPlayer.setVolume(1.0f, 1.0f); // we can be loud

        if (!mPlayer.isPlaying())
            mPlayer.start();
    }

    void processAddRequest(Intent intent) {
        // user wants to play a song directly by URL or path. The URL or path
        // comes in the "data"
        // part of the Intent. This Intent is sent by {@link MainActivity} after
        // the user
        // specifies the URL/path via an alert box.
        if (mState == State.Retrieving) {
            // we'll play the requested URL right after we finish retrieving
            mWhatToPlayAfterRetrieve = intent.getData();
            mStartPlayingAfterRetrieve = true;
        } else if (mState == State.Playing || mState == State.Paused || mState == State.Stopped) {
            Log.i(TAG, "Playing from URL/path: " + intent.getData().toString());
            tryToGetAudioFocus();
            playNextSong(intent.getData().toString());
        }
    }

    void tryToGetAudioFocus() {
        if (mPodStatus == podStat.ADVANCEDHACK)
            return;

        if (mAudioFocus != AudioFocus.Focused && mAudioFocusHelper != null && mAudioFocusHelper.requestFocus())
            mAudioFocus = AudioFocus.Focused;
    }

    /**
     * Starts playing the next song. If manualUrl is null, the next song will be
     * randomly selected from our Media Retriever (that is, it will be a random
     * song in the user's device). If manualUrl is non-null, then it specifies
     * the URL or path to the song that will be played next.
     */
    void playNextSong(String manualUrl) {
        if (mPodStatus == podStat.ADVANCEDHACK) {

            if ((mNowPlaying == 2 && mPrevPlaying == 1) || (mNowPlaying == 0 && mPrevPlaying == 2)
                    || (mNowPlaying == 1 && mPrevPlaying == 0)) {
                broadcastMediaButtons(KeyEvent.KEYCODE_MEDIA_NEXT, mAdvancedRemoteApp);
            }
            if ((mNowPlaying == 1 && mPrevPlaying == 2) || (mNowPlaying == 0 && mPrevPlaying == 1)
                    || (mNowPlaying == 2 && mPrevPlaying == 0)) {
                broadcastMediaButtons(KeyEvent.KEYCODE_MEDIA_PREVIOUS, mAdvancedRemoteApp);
            }
            if (mNowPlaying == mPrevPlaying && mPrevPlaying == 0) {
                broadcastMediaButtons(KeyEvent.KEYCODE_MEDIA_PLAY, mAdvancedRemoteApp);
            }

            mPrevPlaying = mNowPlaying;
            return;
        }

        mState = State.Stopped;
        relaxResources(false); // release everything except MediaPlayer

        MusicRetriever.Track playingItem = null;
        if (manualUrl != null) {
            // set the source of the media player to a manual URL or path
            createMediaPlayerIfNeeded();
            mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            try {
                mPlayer.setDataSource(manualUrl);
            } catch (Exception e) {

            }
            mIsStreaming = manualUrl.startsWith("http:") || manualUrl.startsWith("https:");

            playingItem = new Track(0, null, manualUrl, null, null, 0);
        } else {
            mIsStreaming = false; // playing a locally available song

            boolean IoErrorFlag = true;
            while (IoErrorFlag) {
                if (mNowPlaying >= 0 && mNowPlaying < mRetriever.getCount()) {
                    playingItem = mRetriever.getTrack(mNowPlaying);
                    if (playingItem == null) {
                        mNowPlaying++;
                    }
                } else {
                    mRetriever.switchToMainPlaylist();
                    mNowPlaying = 0;
                    return;
                }

                // set the source of the media player to a content URI

                createMediaPlayerIfNeeded();
                mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
                try {

                    mPlayer.setDataSource(getApplicationContext(), playingItem.getURI());

                    IoErrorFlag = false;

                } catch (IllegalStateException e) {
                    mPlayer.reset();
                } catch (IOException f) {
                    mNowPlaying++;
                }
            }
        }

        mSongTitle = playingItem.getTitle();
        mAlbumArtUri = playingItem.getAlbumArtUri();
        mElapsedTime = 0;

        mState = State.Preparing;
        setUpAsForeground(mSongTitle + getString(R.string.notify_loading));

        // Use the media button APIs (if available) to register ourselves
        // for media button
        // events

        // starts preparing the media player in the background. When it's
        // done, it will call
        // our OnPreparedListener (that is, the onPrepared() method on this
        // class, since we set
        // the listener to 'this').
        //
        // Until the media player is prepared, we *cannot* call start() on
        // it!

        int ioError = 0;
        while (ioError < 5) {
            try {
                mPlayer.prepareAsync();
                ioError = 5;
            } catch (NullPointerException e) {
                createMediaPlayerIfNeeded();
                mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
                try {
                    mPlayer.setDataSource(getApplicationContext(), playingItem.getURI());
                } catch (IllegalStateException e1) {
                    mPlayer.reset();
                    ioError++;
                } catch (IOException f) {
                    f.printStackTrace();
                    ioError = 5;
                }

            } catch (IllegalStateException e1) {
                mPlayer.reset();
                ioError++;
            }
        }

        mBanner = getString(R.string.now_playing);

        localBroadcast(false);

        // If we are streaming from the internet, we want to hold a Wifi
        // lock, which prevents
        // the Wifi radio from going to sleep while the song is playing. If,
        // on the other hand,
        // we are *not* streaming, we want to release the lock if we were
        // holding it before.
        if (mIsStreaming)
            mWifiLock.acquire();
        else if (mWifiLock.isHeld())
            mWifiLock.release();

    }

    /** Called when media player is done playing current song. */
    public void onCompletion(MediaPlayer player) {
        // The media player finished playing the current song, so we go ahead
        // and start the next.
        mChangedCounter = 1;
        processSkipRequest();
    }

    /** Called when media player is done preparing. */
    public void onPrepared(MediaPlayer player) {
        // The media player is done preparing. That means we can start playing!
        mState = State.Playing;
        updateNotification(mSongTitle + getString(R.string.notify_playing));
        configAndStartMediaPlayer();
    }

    /** Updates the notification. */
    void updateNotification(String text) {
        PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0,
                new Intent(getApplicationContext(), MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);

        mNotification = new Notification.Builder(getApplicationContext()).setContentTitle("PodMode")
                .setContentText(text).setContentIntent(pi).setSmallIcon(R.drawable.ic_stat_podmode)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher)).setOngoing(true)
                .getNotification();

        mNotificationManager.notify(NOTIFICATION_ID, mNotification);
    }

    /**
     * Configures service as a foreground service. A foreground service is a
     * service that's doing something the user is actively aware of (such as
     * playing music), and must appear to the user as a notification. That's why
     * we create the notification here.
     */
    void setUpAsForeground(String text) {
        PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0,
                new Intent(getApplicationContext(), MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);

        mNotification = new Notification.Builder(getApplicationContext()).setContentTitle("PodMode")
                .setContentText(text).setContentIntent(pi).setSmallIcon(R.drawable.ic_stat_podmode)
                .setContentIntent(pi)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher)).setOngoing(true)
                .getNotification();

        startForeground(NOTIFICATION_ID, mNotification);
    }

    /**
     * Called when there's an error playing media. When this happens, the media
     * player goes to the Error state. We warn the user about the error and
     * reset the media player.
     */
    public boolean onError(MediaPlayer mp, int what, int extra) {
        Toast.makeText(getApplicationContext(), "Media player error! Resetting.", Toast.LENGTH_SHORT).show();
        Log.e(TAG, "Error: what=" + String.valueOf(what) + ", extra=" + String.valueOf(extra));

        mState = State.Stopped;
        relaxResources(true);
        giveUpAudioFocus();
        return true; // true indicates we handled the error
    }

    public void onGainedAudioFocus() {
        // Toast.makeText(getApplicationContext(), "gained audio focus.",
        // Toast.LENGTH_SHORT).show();
        mAudioFocus = AudioFocus.Focused;

        // restart media player with new focus settings
        if (mState == State.Playing)
            configAndStartMediaPlayer();
    }

    public void onLostAudioFocus(boolean canDuck) {
        // Toast.makeText(getApplicationContext(),
        // "lost audio focus." + (canDuck ? "can duck" : "no duck"),
        // Toast.LENGTH_SHORT).show();
        mAudioFocus = canDuck ? AudioFocus.NoFocusCanDuck : AudioFocus.NoFocusNoDuck;

        // start/restart/pause media player with new focus settings
        if (mPlayer != null && mPlayer.isPlaying())
            configAndStartMediaPlayer();
    }

    private void localBroadcast(boolean launchMainActivity) {
        Intent localIntent = new Intent(MainActivity.BANNER);
        localIntent.putExtra("banner", mBanner);
        if (!mSongTitle.equals("")) {
            localIntent.putExtra("songname", mSongTitle);
            localIntent.putExtra("albumarturi", mAlbumArtUri);
        }

        if (launchMainActivity) {
            Intent launchIntent = new Intent(getBaseContext(), MainActivity.class);
            launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            launchIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

            getApplication().startActivity(launchIntent);
        }

        LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
    }

    @Override
    public void onDestroy() {
        // Service is being killed, so make sure we release our resources
        mState = State.Stopped;
        mPodRunning = false;

        AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);

        if ((mPodStatus == podStat.SIMPLEREMOTE || mPodStatus == podStat.ADVANCEDHACK) && am.isMusicActive()) {
            broadcastMediaButtons(KeyEvent.KEYCODE_MEDIA_STOP, null);
        }

        // Stop the timer task
        if (mMediaChangeTimer != null)
            mMediaChangeTimer.cancel();

        // Stop the main thread
        if (mMainThread != null) {
            Thread moribund = mMainThread;
            mMainThread = null;
            moribund.interrupt();
        }

        // mProvider.dropRemoteControls(true);

        relaxResources(true);
        giveUpAudioFocus();

        if (mSerialHost != null)
            mSerialHost.end();

        if (mSerialDevice != null)
            mSerialDevice.DestroyAccessory(true);

        if (mBTDevice != null)
            mBTDevice.stop();

        if (podWakeLock != null)
            podWakeLock.release();

        unregisterReceiver(mUsbReceiver);
        LocalBroadcastManager.getInstance(this).unregisterReceiver(mNotifyReceiver);

        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);

        mRetriever.saveState(prefs, mNowPlaying);
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

}