public interface CommandCallback {

    public void onResponse();

} public class ConnectionMessage implements Parcelable {

    private String topLine;
    private String middleLine;
    private String bottomLine;

    public ConnectionMessage(String topLine, String middleLine, String bottomLine) {
        this.setTopLine(topLine);
        this.setMiddleLine(middleLine);
        this.setBottomLine(bottomLine);
    }


    public String getTopLine() {
        return topLine;
    }

    public void setTopLine(String topLine) {
        this.topLine = topLine;
    }

    public String getMiddleLine() {
        return middleLine;
    }

    public void setMiddleLine(String middleLine) {
        this.middleLine = middleLine;
    }

    public String getBottomLine() {
        return bottomLine;
    }

    public void setBottomLine(String bottomLine) {
        this.bottomLine = bottomLine;
    }

    protected ConnectionMessage(Parcel in) {
        topLine = in.readString();
        middleLine = in.readString();
        bottomLine = in.readString();
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(topLine);
        dest.writeString(middleLine);
        dest.writeString(bottomLine);
    }

    @SuppressWarnings("unused")
    public static final Parcelable.Creator<ConnectionMessage> CREATOR = new Parcelable.Creator<ConnectionMessage>() {
        @Override
        public ConnectionMessage createFromParcel(Parcel in) {
            return new ConnectionMessage(in);
        }

        @Override
        public ConnectionMessage[] newArray(int size) {
            return new ConnectionMessage[size];
        }
    };
} public abstract class ConnectorUpdateCallback extends ResultReceiver {

    public static final int RC_DATA_UPDATED = 0;
    public static final int RC_DATA_UNAVAILABLE = 1;
    public static final int RC_PLAYING_STATE_CHANGED = 2;
    public static final int RC_PLAYLIST_UPDATED = 3;

    public static final String BUNDLE_SONG = "SONG";
    public static final String BUNDLE_STATE = "STATE";
    public static final String BUNDLE_ERROR_CODE = "ERROR_CODE";
    public static final String BUNDLE_CONNECTION_MESSAGE = "CONNECTION_MESSAGE";

    public ConnectorUpdateCallback() {
        super(new Handler());
    }

    @Override
    protected void onReceiveResult(int resultCode, Bundle resultData) {
        super.onReceiveResult(resultCode, resultData);
        switch (resultCode) {
            case RC_DATA_UPDATED:
                onDataUpdated((Song)resultData.getParcelable(BUNDLE_SONG),
                        PlayingState.values()[resultData.getInt(BUNDLE_STATE)]);
                break;
            case RC_DATA_UNAVAILABLE:
                onDataUnavailable(resultData.getInt(BUNDLE_ERROR_CODE),
                        (ConnectionMessage)resultData.getParcelable(BUNDLE_CONNECTION_MESSAGE));
                break;
            case RC_PLAYING_STATE_CHANGED:
                onPlayingStateChanged(PlayingState.values()[resultData.getInt(BUNDLE_STATE)]);
                break;

        }
    }

    /**
     *  Called in update loop on successfull data update.
     *  Also called in case of
     * @param song Currently playing song. Can be null
     * @param state Playback state
     */
    public abstract void onDataUpdated(Song song, PlayingState state);

    /**
     * Called in update loop in case of error
     * @param errorCode code describing error
     * @param connectionMessage
     */
    public abstract void onDataUnavailable(int errorCode, ConnectionMessage connectionMessage);

    /**
     * Called if playing state has changed
      * @param state new playing state
     */
    public abstract void onPlayingStateChanged(PlayingState state);

} public enum AmarokCommand implements Command {

    NEXT("/cmdNext/"), PREV("/cmdPrev/"), PLAY("/cmdPlay/"), PAUSE("/cmdPause/"),
    PLAY_PAUSE("/cmdPlayPause/"), STOP("/cmdStop/"), VOL_UP("/cmdVolumeUp/"),
    VOL_DOWN("/cmdVolumeDown/"), MUTE("/cmdMute/"), PLAY_BY_INDEX("/cmdPlayByIndex/"),
    REMOVE_BY_INDEX("/cmdRemoveByIndex/"), SET_POSITION("/cmdSetPosition/"),
    COLLECTION_ENQUEUE("/cmdCollectionEnqueue/"), PLAYLIST_CLEAR("/cmdPlaylistClear/");

    private String mCommand;
    private String mParameter = "";
    private CommandCallback mCallback;

    private AmarokCommand(String command) {
        this.mCommand = command;
    }

    @Override
    public String getCommand() {
        return mCommand;
    }

    public Command param(String param) {
        if (param != null) mParameter = param;
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void execute() {
        execute("");
    }

    public void execute(String params) {
        String url = Prefs.getIp();
        if (TextUtils.isEmpty(url)) return;

        OkHttpClient client = new OkHttpClient();
        String com = url + mCommand + mParameter + params;
        Request request = new Request.Builder().url(com).build();
        Log.e("", "COMMAND: " + com);
        client.newCall(request).enqueue(new Callback() {
            @Override public void onFailure(Request request, IOException e) {
                runCallbackInMainThread();
                Log.e("", request.toString());
            }
            @Override public void onResponse(Response response) throws IOException {
                runCallbackInMainThread();
                Log.e("", response.toString());
            }
        });
    }

    private void runCallbackInMainThread() {
        if (mCallback != null) {
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    mCallback.onResponse();
                }
            });
        }
    }

    @Override
    public Command withCallback(CommandCallback callback) {
        mCallback = callback;
        return this;
    }
} public class BitmapConcurrencyHandler {

	protected LruCache<String, Bitmap> cache;
	Context context;
	private static Bitmap emptyCover;
	Animation anim_in;
	
	public BitmapConcurrencyHandler(Context context) {
		this.context = context;
		emptyCover = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_collection_artist1);
		
		
		final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
		final int cacheSize = maxMemory / 16 ;
		cache = new LruCache<String, Bitmap>(cacheSize){
			@SuppressLint("NewApi")
			@Override
			protected int sizeOf(String key, Bitmap bitmap) {
				if (Integer.valueOf(android.os.Build.VERSION.SDK_INT) >= 12)
		            return bitmap.getByteCount() / 1024;
		        else
		            return (bitmap.getRowBytes() * bitmap.getHeight()) / 1024;
			}
		};
	}
	
	
	
	private void addBitmapToMemoryCache(String key, Bitmap bitmap) {		
	    if (getBitmapFromMemCache(key) == null) {
	    	// create scaled version of bitmap
	        cache.put(key, bitmap);
	    }
	}

	private Bitmap getBitmapFromMemCache(String key) {
		Bitmap ret;
		try {
			ret = cache.get(key);
		} catch (NullPointerException ex) {
			ret = null;
		}
	    return ret; 
	}
	
	
	public void loadBitmap(String key, ImageView imageView) {
		Bitmap cached = getBitmapFromMemCache(key);
		if(cached != null) {
			imageView.setImageBitmap(cached);
		} else if (cancelPotentialWork(key, imageView)) {
	        final BitmapWorkerTask task = new BitmapWorkerTask(imageView, key);
	        final AsyncDrawable asyncDrawable = new AsyncDrawable(task);
	        imageView.setImageDrawable(asyncDrawable);
	        task.execute();
	    }
	}
	
	
	public static boolean cancelPotentialWork(String key, ImageView imageView) {
	    final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

	    if (bitmapWorkerTask != null) {
	    	String bitmapUrl = null;
        	bitmapUrl = bitmapWorkerTask.key;
        	
	        if ((bitmapUrl == null) || (!bitmapUrl.equals(key))) {
	        	bitmapWorkerTask.cancel(true);
	        } else {
	            // The same URL is already being downloaded.
	            return false;
	        }
	    }
	    // No task associated with the ImageView, or an existing task was cancelled
	    return true;
	}
	
	private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
		if (imageView != null) {
			final Drawable drawable = imageView.getDrawable();
			if (drawable instanceof AsyncDrawable) {
				final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
				return asyncDrawable.getBitmapWorkerTask();
			}
		}
		return null;
	}

	static class AsyncDrawable extends BitmapDrawable {
		private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;

		@SuppressWarnings("deprecation")
		public AsyncDrawable(BitmapWorkerTask bitmapWorkerTask) {
			super(emptyCover);
			bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
		}

		public BitmapWorkerTask getBitmapWorkerTask() {
			return bitmapWorkerTaskReference.get();
		}
	}
	

	
	
	
	
	
	
	
	
	
	
	
	private class BitmapWorkerTask extends AsyncTask<Long, Void, Bitmap> {

		public String key;
		private final WeakReference<ImageView> imageViewReference;
		Animation anim_in = AnimationUtils.loadAnimation(context, android.R.anim.fade_in);

		public BitmapWorkerTask(ImageView imageView, String key) {
			// Use a WeakReference to ensure the ImageView can be garbage
			// collected
			imageViewReference = new WeakReference<ImageView>(imageView);
			this.key = key;
		}

		// Decode image in background.
		@Override
		protected Bitmap doInBackground(Long... params) {
			Bitmap loaded = null;
			loaded = MediaHelper.readFromCache(key, context);
			if(loaded == null) return null;
			loaded = BitmapHelper.scaleToFill(loaded, context.getResources().getInteger(R.integer.artist_mini_photo_dimen), context.getResources().getInteger(R.integer.artist_mini_photo_dimen));
			
			if (loaded != null) addBitmapToMemoryCache(key, loaded);
			
			return loaded;
		}

		@Override
		protected void onPostExecute(Bitmap bitmap) {
			if (isCancelled()) {
				bitmap = null;
			}
			if (imageViewReference != null && bitmap != null) {
				final ImageView imageView = imageViewReference.get();
				final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

				if (this == bitmapWorkerTask && imageView != null) {
					imageView.startAnimation(anim_in);
					imageView.setImageBitmap(bitmap);
				}
			}
		}
	}
	
} public class MediaHelper {


    public static Bitmap downloadBitmap(String url, String cacheTag, Context context) {
        return downloadBitmap(url, cacheTag, context, false);
    }

    public static Bitmap downloadBitmap(String url, String cacheTag, Context context, boolean createMini) {
        cacheTag = FileHelper.getSafeString(cacheTag);

        Bitmap bitmap;
        bitmap = readFromCache(cacheTag, context);
        if (bitmap != null) return bitmap;

        try {
            OkHttpClient client = new OkHttpClient();
            Request request = new Request.Builder().url(url).build();

            Response response = client.newCall(request).execute();

            if (!response.isSuccessful()) {
                return null;
            }
            InputStream inputStream = null;
            try {
                inputStream = response.body().byteStream();
                bitmap = BitmapFactory.decodeStream(inputStream);
                if (bitmap == null && (bitmap = readFromCache(cacheTag, context)) != null)
                    return bitmap;
                if (cacheTag != null && bitmap != null)
                    saveToCache(bitmap, cacheTag, context, createMini);
                return bitmap;
            } finally {
                if (inputStream != null) {
                    inputStream.close();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Bitmap getBluredBitmap(String cacheTag, Bitmap bitmapToBlur, Context context) {
        if (bitmapToBlur == null)
            return BitmapFactory.decodeResource(context.getResources(), R.drawable.albumblured);

        cacheTag = FileHelper.getSafeString(cacheTag);
        Bitmap bitmap = readFromCache(cacheTag + "blured", context);

        if (bitmap != null)
            return bitmap;

        bitmap = BitmapHelper.fastblur(bitmapToBlur, Prefs.blurIntensity);
        if(FileHelper.isExternalStorageWritable()) {
            File file = new File(context.getExternalCacheDir().toString() + "/"+cacheTag+"blured.jpg");
            if(!file.exists())
                saveToCache(bitmap, cacheTag + "blured", context, false);
        }
        return bitmap;
    }

    private static void saveToCache(Bitmap cover, String cacheTag, Context context, boolean createMini) {
        if(!FileHelper.isExternalStorageWritable()) return;
        cacheTag = FileHelper.getSafeString(cacheTag);

        FileOutputStream fo;
        try {
            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
            cover.compress(Bitmap.CompressFormat.PNG, 0, bytes);
            context.getExternalCacheDir().mkdirs();

            File f = new File(context.getExternalCacheDir().toString() + File.separator + cacheTag + ".jpg");
            f.createNewFile();

            fo = new FileOutputStream(f);
            fo.write(bytes.toByteArray());
            fo.close();

            if(createMini) {
                f = new File(context.getExternalCacheDir().toString() + File.separator + cacheTag + "_mini.jpg");
                if(f.exists()) return;
                f.createNewFile();
                bytes = new ByteArrayOutputStream();
                cover = BitmapHelper.scaleToFitWidth(cover, BitmapHelper.convertDPtoPX(128, context));
                cover.compress(Bitmap.CompressFormat.PNG, 0, bytes);

                fo = new FileOutputStream(f);
                fo.write(bytes.toByteArray());
                fo.close();
            }

        } catch (FileNotFoundException e) {
        } catch (IOException e) {
        }
    }

    public static Bitmap readFromCache(String cacheTag, Context context) {
        if(!FileHelper.isExternalStorageReadable()) return null;
        cacheTag = FileHelper.getSafeString(cacheTag);
        try {
            File file = new File(context.getExternalCacheDir().toString() + File.separator + cacheTag + ".jpg");
            FileInputStream fis = new FileInputStream(file);

            Bitmap cover = BitmapFactory.decodeStream(fis);
            fis.close();
            return cover;
        } catch (FileNotFoundException e) {
        } catch (IOException e) {
        } catch (NullPointerException ex) {
        }
        return null;
    }

    public static void downloadSong(final int pos, final Song song, final BaseActivity activity) {

    new AsyncTask<Void, Integer, Void>(){

        protected void onPreExecute() {
            activity.setActionBarInfoCancelTask(this);
            activity.showActionBarInfo(this);
            activity.setActionBarInfoText(activity.getString(R.string.navbar_downloading_song)+"0%", this);
        }

        protected void onPostExecute(Void result) {
            activity.hideActionBarInfo(this);
        }

        @Override
        protected Void doInBackground(Void... param) {
            int count;
            try {
                URL url = new URL(Prefs.getIp()+"/getSongFile/"+pos);
                URLConnection conexion = url.openConnection();
                conexion.connect();
                // this will be useful so that you can show a tipical 0-100% progress bar
                int lenghtOfFile = conexion.getContentLength();
                String mimeType = conexion.getContentType();
                // downlod the file
                InputStream input = new BufferedInputStream(url.openStream());

                String savePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + Environment.DIRECTORY_MUSIC + File.separator ;
                savePath += song.getArtist() + File.separator + song.getAlbum();

                new File(savePath).mkdirs();

                OutputStream output = new FileOutputStream(savePath + File.separator + song.getTitle() + "." + mimeType);

                byte data[] = new byte[1024];

                long total = 0;

                while ((count = input.read(data)) != -1) {
                    total += count;
                    // publishing the progress....
                    publishProgress((int)(total*100/lenghtOfFile));
                    output.write(data, 0, count);
                }
                output.flush();
                output.close();
                input.close();
                new MediaScannerWrapper(savePath, activity).scan();
            } catch (Exception e) {
            }
            return null;
        }

        int prev = 0;
        protected void onProgressUpdate(Integer[] values) {
            if(prev < values[0]) {
                prev = values[0];
                activity.setActionBarInfoText(activity.getString(R.string.navbar_downloading_song)+values[0]+"%", this);
            }
        }

    }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
    }

    private static class MediaScannerWrapper implements MediaScannerConnection.MediaScannerConnectionClient{
        MediaScannerConnection connection;
        String path;
        public MediaScannerWrapper(String path, Context context) {
            connection = new MediaScannerConnection(context, this);
            this.path = path;
        }
        public void scan() {connection.connect();}
        @Override public void onMediaScannerConnected() { connection.scanFile(path, null); }
        @Override public void onScanCompleted(String path, Uri uri) { connection.disconnect(); }
    }
} public enum PlayingState {

    PLAYING, PAUSE, NOTPLAYING, DOWN;

    public static PlayingState state = DOWN;

} public class Song implements Parcelable {

    private String title;
    private String artist;
    private String album;
    private int id;
    private Bitmap cover;
    private Bitmap bluredCover;
    private boolean isNewAlbum = false;
    private String lyrics;
    private int length;
    private int position;

    public Song(String title, String artist, String album){
        this.title = title;
        this.artist = artist;
        this.album = album;
    }

    public Song(String title, String artist, String album, Bitmap cover){
        this(title, artist, album);
        this.cover = cover;
    }

    public Song(int id, String title, String artist, String album) {
        this(title, artist, album);
        this.id = id;
    }

    @Override
    public boolean equals(Object o) {
        Song obj = (Song)o;
        if(obj == null) return false;
        return title.equals(obj.getTitle()) && artist.equals(obj.getArtist()) && album.equals(obj.getAlbum());
    }

    @Override
    public String toString() {
        return title+artist+album;
    }

    public void setNewAlbum(boolean newAlbum) {
        isNewAlbum = newAlbum;
    }

    public boolean isNewAlbum() {
        return isNewAlbum;
    }

    public boolean sameSong(Song song) {
        return title.equals(song.title) && artist.equals(song.artist);
    }

    public boolean sameSong(String title, String artist, String album) {
        return this.title.equals(title) && this.artist.equals(artist) && this.album.equals(album);
    }

    public boolean onSameAlbum(Song song){
        return song.album.equals(album) && song.artist.equals(artist);
    }

    public boolean onSameAlbum(String artist, String album) {
        return this.artist.equals(artist) && this.album.equals(album);
    }

    public String getTitle() {
        return title;
    }

    public String getArtist() {
        return artist;
    }

    public String getAlbum() {
        return album;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public void setArtist(String artist) {
        this.artist = artist;
    }

    public void setAlbum(String album) {
        this.album = album;
    }

    public void setCover(Bitmap cover) {
        this.cover = cover;
    }

    public int getId() {
        return id;
    }

    public Bitmap getCover() {
        return cover;
    }

    public void setLyrics(String lyrics) {
        this.lyrics = lyrics;
    }

    public String getLyrics() {
        return lyrics;
    }

    public Bitmap getBlured() {
        return bluredCover;
    }

    public void setBluredCover(Bitmap blured) {
        bluredCover = blured;
    }

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    public int getPosition() {
        return position;
    }

    public void setPosition(int position) {
        this.position = position;
    }

    protected Song(Parcel in) {
        title = in.readString();
        artist = in.readString();
        album = in.readString();
        id = in.readInt();
        cover = (Bitmap) in.readValue(Bitmap.class.getClassLoader());
        bluredCover = (Bitmap) in.readValue(Bitmap.class.getClassLoader());
        isNewAlbum = in.readByte() != 0x00;
        lyrics = in.readString();
        length = in.readInt();
        position = in.readInt();
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(title);
        dest.writeString(artist);
        dest.writeString(album);
        dest.writeInt(id);
        dest.writeValue(cover);
        dest.writeValue(bluredCover);
        dest.writeByte((byte) (isNewAlbum ? 0x01 : 0x00));
        dest.writeString(lyrics);
        dest.writeInt(length);
        dest.writeInt(position);
    }

    public static final Parcelable.Creator<Song> CREATOR = new Parcelable.Creator<Song>() {
        @Override
        public Song createFromParcel(Parcel in) {
            return new Song(in);
        }

        @Override
        public Song[] newArray(int size) {
            return new Song[size];
        }
    };


} public class BaseActivity extends FragmentActivity {

    private BaseFragment[] mFragments;
    private static Stack<Integer> sFragmentStack = new Stack<>();
    private static int sCurrentFragment;

	protected SlidingMenu mSlidingMenu;
    private boolean isMenuAvailable;
	
	private SwipeListView mPlayListView;
	private static Bitmap sBackground;
	private RadioGroup mRadioGroup;
	private RelativeLayout mActionBarStatusLayout;
	private TextView mActionBarStatusText;
	private ImageButton mActionBarStatusButton, mActionBarSearch;
	private static AsyncTask sActionBarTask;

	public PlaylistAdapter mPlaylistAdapter;

    public void pushFragment(int fragmentId, Bundle arguments) {
        sFragmentStack.push(sCurrentFragment);
        sCurrentFragment = fragmentId;
        switchFragmentVisibility(fragmentId, arguments);
    }

    public void popFragment(int fragmentId) {
        while(sFragmentStack.pop() != fragmentId);
        sCurrentFragment = fragmentId;
        switchFragmentVisibility(fragmentId, null);
    }

    private void switchFragmentVisibility(int fragmentId, Bundle arguments) {
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        transaction.disallowAddToBackStack();
        BaseFragment fragmentToShow = null;

        for (BaseFragment fragment : mFragments) {
            transaction.hide(fragment);
            transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE);
            if(fragment.getId() == fragmentId) fragmentToShow = fragment;
        }

        if(arguments != null) fragmentToShow.setBundle(arguments);
        transaction.show(fragmentToShow);
        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        transaction.commit();
    }

	protected void onCreateActionBar() {
		requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
		ActionBar actionBar = getActionBar();
		actionBar.setBackgroundDrawable(new ColorDrawable(Color.parseColor("#aa000000")));
		actionBar.setDisplayShowHomeEnabled(false);
		actionBar.setDisplayShowTitleEnabled(false);
		actionBar.setCustomView(R.layout.actionbar_navigaion);
		actionBar.setDisplayShowCustomEnabled(true);
		
		View actionBarRootView = actionBar.getCustomView();

		final ImageView collectionIcon = (ImageView) actionBarRootView.findViewById(R.id.nav_tab_collection_icon);
        final Animation off = AnimationUtils.loadAnimation(this, R.anim.scale_off);

		collectionIcon.startAnimation(off);
		
		mRadioGroup = (RadioGroup) actionBar.getCustomView().findViewById(R.id.nav_tab_radios);
		mActionBarStatusLayout = (RelativeLayout) actionBar.getCustomView().findViewById(R.id.actionbar_status);
		mActionBarStatusText = (TextView) mActionBarStatusLayout.findViewById(R.id.actionbar_status_message);
		mActionBarStatusButton = (ImageButton) mActionBarStatusLayout.findViewById(R.id.actionbar_status_button);
		mActionBarSearch = (ImageButton) actionBarRootView.findViewById(R.id.navigation_buttons_search);
		mActionBarSearch.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                pushFragment(R.id.fragSearch, null);
            }
        });
	}

    @Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		onCreateActionBar();
		setContentView(R.layout.content_frame);

        if (!Storage.getBoolValue(Constants.CONST_UPDATE_SCRIPT)) {
            AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
            View dialogLayout = View.inflate(this, R.layout.dialog_update, null);
            dialogLayout.findViewById(R.id.githubImage).setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Constants.URL_GITHUB)));
                }
            });
            dialogBuilder.setView(dialogLayout);
            dialogBuilder.show();
            Storage.storeBoolValue(Constants.CONST_UPDATE_SCRIPT, true);
        }

        if(getResources().getBoolean(R.bool.IsPhone))
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

        FragmentManager fm = getSupportFragmentManager();
        mFragments = new BaseFragment[]{
                (BaseFragment) fm.findFragmentById(R.id.fragPlayNow),
                (BaseFragment) fm.findFragmentById(R.id.fragCollection),
                (BaseFragment) fm.findFragmentById(R.id.fragArtistDetail),
                (BaseFragment) fm.findFragmentById(R.id.fragSearch)
        };


        ActionBar actionBar = getActionBar();
        View actionBarRootView = actionBar.getCustomView();

        final ImageView playNowIcon = (ImageView) actionBarRootView.findViewById(R.id.nav_tab_play_now_icon);
        final ImageView collectionIcon = (ImageView) actionBarRootView.findViewById(R.id.nav_tab_collection_icon);

        final View playNowHightligh = actionBarRootView.findViewById(R.id.nav_tab_play_now_highlight);
        final View collectionHighlight = actionBarRootView.findViewById(R.id.nav_tab_collection_highlight);

        final Animation on = AnimationUtils.loadAnimation(this, R.anim.scale_on);
        final Animation off = AnimationUtils.loadAnimation(this, R.anim.scale_off);


        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        transaction.disallowAddToBackStack();

        for (BaseFragment fragment : mFragments) {
            transaction.hide(fragment);
        }

		if(savedInstanceState != null) {
			imageViewAnimatedChange(sBackground);
            if (sCurrentFragment != R.id.fragPlayNow) {
                ((RadioButton) mRadioGroup.findViewById(R.id.nav_tab_radio_collection)).setChecked(true);
            }
            transaction.show(fm.findFragmentById(sCurrentFragment));

            playNowHightligh.setVisibility(View.INVISIBLE);
            collectionHighlight.setVisibility(View.VISIBLE);
            collectionIcon.startAnimation(on);
            playNowIcon.startAnimation(off);
            setSearchButtonVisibile(true);
        } else {
            sCurrentFragment = R.id.fragPlayNow;
            transaction.show(fm.findFragmentById(R.id.fragPlayNow));
            collectionIcon.startAnimation(off);
        }

        transaction.commit();


        mPlaylistAdapter = new PlaylistAdapter(this, 0, new ArrayList<Song>());

		mSlidingMenu = new SlidingMenu(this);
		mSlidingMenu.setShadowWidthRes(R.dimen.shadow_width);
		mSlidingMenu.setShadowDrawable(R.drawable.shsc);
        mSlidingMenu.setBehindOffsetRes(getResources().getBoolean(R.bool.IsSmallTablet) ?
                R.dimen.slidingmenu_tablet_offset : R.dimen.slidingmenu_offset);
		mSlidingMenu.setFadeDegree(0.35f);
		mSlidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
		mSlidingMenu.setMode(SlidingMenu.RIGHT);
		mSlidingMenu.attachToActivity(this, SlidingMenu.SLIDING_WINDOW);

		if(isMenuAvailable = (findViewById(R.id.playlist_frame) == null)) {
			mSlidingMenu.setMenu(R.layout.playlist_layout);
		} else {
			mSlidingMenu.setSlidingEnabled(false);
		}

        View emptyView = findViewById(R.id.fragment_play_now_playlist_empty_view);
        mPlayListView = (SwipeListView) findViewById(R.id.playlistView);
        mPlayListView.setAdapter(mPlaylistAdapter);
		mPlayListView.setTag("reset");
		mPlayListView.setMultiChoiceModeListener(mMultiChoiceModeListener);
		mPlayListView.setSwipeListViewListener(mBaseSwipeListViewListener);
        mPlayListView.setEmptyView(emptyView);

        emptyView.setOnClickListener(onClickListener);
		findViewById(R.id.playlist_bottom_menu_refresh).setOnClickListener(onClickListener);
		findViewById(R.id.playlist_bottom_menu_jump).setOnClickListener(onClickListener);
        findViewById(R.id.playlist_bottom_menu_clear).setOnClickListener(onClickListener);

        mRadioGroup = (RadioGroup) actionBar.getCustomView().findViewById(R.id.nav_tab_radios);
        mRadioGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                boolean isChecked = ((RadioButton) group.findViewById(checkedId)).isChecked();
                if (!isChecked) return;
                switch (checkedId) {
                    case R.id.nav_tab_radio_play_now:
                        playNowHightligh.setVisibility(View.VISIBLE);
                        collectionHighlight.setVisibility(View.INVISIBLE);
                        playNowIcon.startAnimation(on);
                        collectionIcon.startAnimation(off);

                        setSearchButtonVisibile(false);
                        // do not allow to "check" if it's already checked
                        if(sFragmentStack.size() >= 1) popFragment(R.id.fragPlayNow);
                        break;
                    case R.id.nav_tab_radio_collection:
                        playNowHightligh.setVisibility(View.INVISIBLE);
                        collectionHighlight.setVisibility(View.VISIBLE);
                        collectionIcon.startAnimation(on);
                        playNowIcon.startAnimation(off);

                        setSearchButtonVisibile(true);
                        pushFragment(R.id.fragCollection, null);
                        break;
                }
            }
        });

        refreshPlaylist();
	}

    private OnClickListener onClickListener = new OnClickListener() {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.playlist_bottom_menu_refresh:
                case R.id.fragment_play_now_playlist_empty_view:
                    refreshPlaylist();
                    break;
                case R.id.playlist_bottom_menu_jump:
                    int currentSongIndex = mPlaylistAdapter.getIndexOfCurrentSong();
                    mPlayListView.smoothScrollToPositionFromTop(currentSongIndex, 200, 300);
                    break;
                case R.id.playlist_bottom_menu_clear:
                    App.getInstance().sendCommand(AmarokCommand.PLAYLIST_CLEAR);
                    refreshPlaylist();
                    break;
            }

        }
    };

    private AbsListView.MultiChoiceModeListener mMultiChoiceModeListener = new AbsListView.MultiChoiceModeListener() {

        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            return false;
        }

        @Override
        public void onDestroyActionMode(ActionMode mode) {
            mPlayListView.unselectedChoiceStates();
        }

        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            return false;
        }

        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            if (item.getItemId() == R.id.playlist_back_view_remove) {
                mPlayListView.dismissSelected();
                return true;
            }
            new ExampleInterface(){

                @Override
                public void doTheJob() {

                }
            };
            return false;
        }

        @Override
        public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {

        }
    };

    private BaseSwipeListViewListener mBaseSwipeListViewListener = new BaseSwipeListViewListener() {
        @Override
        public void onMove(int position, float x) {
            int firstVisible = mPlayListView.getFirstVisiblePosition();
            int lastVisible = mPlayListView.getLastVisiblePosition();
            int count = lastVisible - firstVisible;
            for (int i = 0; i <= count; i++) {
                View backLayout = mPlayListView.getChildAt(i).findViewById(R.id.back);
                if (position - firstVisible == i) {
                    backLayout.setVisibility(View.VISIBLE);
                    backLayout.setAlpha(1f * (x / (float) backLayout.getWidth()));
                }
            }
            super.onMove(position, x);
        }

        @Override
        public void onOpened(int position, boolean toRight) {
            int firstVisible = mPlayListView.getFirstVisiblePosition();
            int lastVisible = mPlayListView.getLastVisiblePosition();
            int count = lastVisible - firstVisible;
            for (int i = 0; i <= count; i++) {
                View backLayout = mPlayListView.getChildAt(i).findViewById(R.id.back);
                View frontLayout = mPlayListView.getChildAt(i).findViewById(R.id.front);
                if (position - firstVisible == i && frontLayout.getX() > 0) {
                    backLayout.setVisibility(View.VISIBLE);
                    backLayout.setAlpha(1f);
                }
            }
        }

        @Override
        public void onClosed(int position, boolean fromRight) {
            int firstVisible = mPlayListView.getFirstVisiblePosition();
            int lastVisible = mPlayListView.getLastVisiblePosition();
            int count = lastVisible - firstVisible;
            for (int i = 0; i <= count; i++) {
                View backLayout = mPlayListView.getChildAt(i).findViewById(R.id.back);
                if (position - firstVisible == i) {
                    backLayout.setVisibility(View.GONE);
                    backLayout.setAlpha(0);

                    RelativeLayout frontLayout = (RelativeLayout) mPlayListView.getChildAt(i).findViewById(R.id.front);
                    frontLayout.setX(0f);
                }
            }
        }

        @Override
        public void onClickFrontView(int position) {
            Song clicked = (Song) mPlayListView.getItemAtPosition(position);
            AmarokCommand.PLAY_BY_INDEX.execute(clicked.getId() + "");
            if (isMenuAvailable && Prefs.closePlaylist) {
                mSlidingMenu.showContent(true);
            }
            mPlayListView.setFocusable(false);
            mPlayListView.setFocusable(true);
            mPlayListView.setFocusableInTouchMode(false);
            mPlayListView.setFocusableInTouchMode(true);
            super.onClickFrontView(position);
        }

        @Override
        public void onClickBackView(int position) {
            mPlayListView.closeOpenedItems();
            super.onClickBackView(position);
        }
    };

    private ConnectorUpdateCallback mConnectorUpdateCallback = new ConnectorUpdateCallback() {
        @Override public void onDataUnavailable(int errorCode, ConnectionMessage connectionMessage) { }
        @Override public void onPlayingStateChanged(PlayingState state) { }

        @Override
        public void onDataUpdated(Song song, PlayingState state) {
            if(song == null || song.getBlured() == null || sBackground == song.getBlured()) return;
            sBackground = song.getBlured();
            imageViewAnimatedChange(sBackground);
        }
    };

    public interface ExampleInterface {
        public void doTheJob();
    }

	@Override
	protected void onResume() {
		super.onResume();
		App.activityResumed();
        App.getInstance().registerConnectorCallback(mConnectorUpdateCallback);
        if(PlayingState.state != PlayingState.DOWN) imageViewAnimatedChange(sBackground);
	}
	
	@Override
	protected void onPause() {
		super.onPause();
		App.activityPaused();
        App.getInstance().unregisterConnectorCallback(mConnectorUpdateCallback);
        mPlaylistAdapter.unregisterConnectorCallback();
	}

	@Override
	public void onBackPressed() {
        try {
            if (sFragmentStack.size() == 1) {
                // if going to show play now just check radio button and it will take care of rest
                ((RadioButton)mRadioGroup.findViewById(R.id.nav_tab_radio_play_now)).setChecked(true);
            } else {
                popFragment(sFragmentStack.peek());
            }
        } catch (EmptyStackException e) {
            // now should quit app
            super.onBackPressed();
        }
    }


    @Override
	public boolean dispatchKeyEvent(KeyEvent event) {
		if(Prefs.useVolButtons) {
				if(event.getAction() == KeyEvent.ACTION_DOWN) { // Only on button down					
					switch (event.getKeyCode()) {
						case KeyEvent.KEYCODE_VOLUME_UP:
                            App.getInstance().sendCommand(AmarokCommand.VOL_UP.param(Prefs.volumeStep + ""));
							break;
						case KeyEvent.KEYCODE_VOLUME_DOWN:
                            App.getInstance().sendCommand(AmarokCommand.VOL_DOWN.param(Prefs.volumeStep + ""));
							break;
						default:
							return super.dispatchKeyEvent(event);
					}					
				} else if(event.getAction() == KeyEvent.ACTION_UP || 
						   event.getAction() == KeyEvent.ACTION_MULTIPLE) { // On button up/multiple
					switch (event.getKeyCode()) {
					case KeyEvent.KEYCODE_VOLUME_UP:
					case KeyEvent.KEYCODE_VOLUME_DOWN:
						return true; // ignore volume up/down on up/multiple action
					default:
						return super.dispatchKeyEvent(event);
					}
				}
				return true;
		}
		return super.dispatchKeyEvent(event);
	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.play_now, menu);
		return true;
	}
	
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch(item.getItemId()) {
		case R.id.menu_settings:
			Intent i = new Intent(this, SettingsActivity.class);
			startActivity(i);
			return true;
		case R.id.menu_help:
			AlertDialog.Builder builder = new AlertDialog.Builder(this);
			builder.setView(getLayoutInflater().inflate(R.layout.dialog_help, null));
			builder.show();
			break;
		case R.id.menu_feature:
			Intent intent = new Intent(Intent.ACTION_SEND);
			intent.setType("message/rfc822");
			intent.putExtra(Intent.EXTRA_EMAIL, new String[] {"depeha@gmail.com"});
			intent.putExtra(Intent.EXTRA_SUBJECT, "[amaroKontrol] Feature request");
			startActivity(Intent.createChooser(intent, "Send Email"));
			break;
		case R.id.menu_bug:
			intent = new Intent(Intent.ACTION_SEND);
			intent.setType("message/rfc822");
			intent.putExtra(Intent.EXTRA_EMAIL, new String[] {"depeha@gmail.com"});
			intent.putExtra(Intent.EXTRA_SUBJECT, "[amaroKontrol] Bug Report");
			startActivity(Intent.createChooser(intent, "Send Email"));
		}
		return false;
	}

    public void recoverBackground() {
        imageViewAnimatedChange(sBackground);
    }

	@SuppressLint("NewApi")
	public void imageViewAnimatedChange(final Bitmap new_image) {
		final ImageView v1 = (ImageView)findViewById(R.id.main_background1);
		final ImageView v2 = (ImageView)findViewById(R.id.main_background2);

		Window window = getWindow();
		window.setBackgroundDrawable(new BitmapDrawable(getResources(), new_image));

	    final Animation anim_out = AnimationUtils.loadAnimation(this, android.R.anim.fade_out);
	    final Animation anim_in  = AnimationUtils.loadAnimation(this, android.R.anim.fade_in);

	    if(v1.getVisibility() == View.INVISIBLE) {
	    	v2.setVisibility(View.INVISIBLE);
	    	v2.startAnimation(anim_out);
	    	anim_out.setAnimationListener(new AnimationListener() {
	    		@Override public void onAnimationStart(Animation animation) {}
	            @Override public void onAnimationRepeat(Animation animation) {}
	            @Override public void onAnimationEnd(Animation animation) {v2.setImageBitmap(null);}
			});
	    	v1.setVisibility(View.VISIBLE);
	    	v1.setImageBitmap(new_image);
	    	v1.startAnimation(anim_in);
	    } else {
	    	v1.setVisibility(View.INVISIBLE);
	    	v1.startAnimation(anim_out);
	    	anim_out.setAnimationListener(new AnimationListener() {
	    		@Override public void onAnimationStart(Animation animation) {}
	            @Override public void onAnimationRepeat(Animation animation) {}
	            @Override public void onAnimationEnd(Animation animation) {v1.setImageBitmap(null);}
			});
	    	v2.setVisibility(View.VISIBLE);
	    	v2.setImageBitmap(new_image);
	    	v2.startAnimation(anim_in);
	    }
	    
	}
	

	public void showActionBarInfo(AsyncTask task) {
		sActionBarTask = task;
		Animation animation = new TranslateAnimation(0 , 0, -mActionBarStatusLayout.getHeight(), 0);
		animation.setDuration(500);
		mActionBarStatusLayout.startAnimation(animation);
		mActionBarStatusLayout.setVisibility(View.VISIBLE);
		mActionBarStatusButton.setVisibility(View.VISIBLE);
	}
	
	public void hideActionBarInfo(AsyncTask task) {
		if(sActionBarTask != task) return;
		if(mActionBarStatusLayout.getVisibility() == View.INVISIBLE) return;
		mActionBarStatusLayout.startAnimation(createHideAnimationForActionBarItem(mActionBarStatusLayout));
		sActionBarTask = null;
	}
	
	private Animation createHideAnimationForActionBarItem(final View view) {
		Animation animation = new TranslateAnimation(0 , 0, 0, -view.getHeight());
		animation.setDuration(500);
		animation.setAnimationListener(new AnimationListener() {
			@Override public void onAnimationStart(Animation animation) { }
			@Override public void onAnimationRepeat(Animation animation) { }
			@Override public void onAnimationEnd(Animation animation) { view.setVisibility(View.GONE); }
		});
		return animation;
	}
	
	public void setActionBarButtonInvisible(AsyncTask task) {
		if(sActionBarTask != task) return;
		mActionBarStatusButton.setVisibility(View.INVISIBLE);
	}
	
	public void setActionBarInfoText(String text, AsyncTask task) {
		if(sActionBarTask != task) return;
		mActionBarStatusText.setText(text);
	}
	
	public void setActionBarInfoCancelTask(final AsyncTask task) {
		mActionBarStatusButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                task.cancel(true);
            }
        });
	}
	
    private void setSearchButtonVisibile(boolean visible) {
        if (visible) {
            Animation animation = new TranslateAnimation(0 , 0, -mActionBarSearch.getHeight(), 0);
            animation.setDuration(500);
            mActionBarSearch.startAnimation(animation);
            mActionBarSearch.setVisibility(View.VISIBLE);
        } else {
            mActionBarSearch.startAnimation(createHideAnimationForActionBarItem(mActionBarSearch));
        }
    }

	public void updatePlaylistItemsPlayIcon() {
		try {
			int currentSongIndex = mPlaylistAdapter.getIndexOfCurrentSong();
			if(currentSongIndex > -1) {
				int firstVisible = mPlayListView.getFirstVisiblePosition();
				int lastVisible = mPlayListView.getLastVisiblePosition();
				int count = lastVisible - firstVisible;
				for(int i = 0; i < count; i++) {
					ImageView playingIcon = (ImageView) mPlayListView.getChildAt(i).findViewById(R.id.listSongPlayingIcon);
					playingIcon.setVisibility((currentSongIndex - firstVisible == i) ? View.VISIBLE : View.GONE);
				}				
			}
		} catch (NullPointerException ex) {}
	}
	
	public void refreshPlaylist() {
        App.getInstance().updatePlaylist(new PlaylistUpdateCallback() {
            @Override
            public void onPlaylistReceived(ArrayList<Song> list) {
                if(list.size() > 0)
                    CacheCoversTask.runTask(list.toArray(new Song[list.size()]));

                mPlaylistAdapter.clear();
                mPlaylistAdapter.addAll(list);

                View emptyView = ((View) mPlayListView.getParent()).findViewById(R.id.fragment_play_now_playlist_empty_view);

                if (list.size() > 0) {
                    emptyView.setVisibility(View.GONE);
                } else {
                    emptyView.setVisibility(View.VISIBLE);
                }

                mPlayListView.requestLayout();
            }
        });
	}
	
} public class CustomTextView extends TextView {

	public enum TextFont {
        BOLD(0, "bold"), BOLD_C(1, "cbold"), LIGHT_C(2, "clight"), REG_C(3, "creg"),
        BLACK(4, "ebold"), LIGHT(5, "light"), MED(6, "med"), REG(7, "reg"), THIN(8, "thin");

        private static final Map<Integer, TextFont> lookup = new HashMap<Integer, TextFont>();
        static {
            for(TextFont font : TextFont.values())
                lookup.put(font.value, font);
        }

        public static TextFont get(int value) {
            return lookup.get(value);
        }

        private int value;
        private String name;
        TextFont(int value, String name) {
            this.value = value;
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public int value() {
            return value;
        }
    }
	
	public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init(attrs);
	}

	public CustomTextView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(attrs);

	}

	public CustomTextView(Context context) {
		super(context);
		init(null);
	}

	private void init(AttributeSet attrs) {
		if (attrs != null) {
			TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CustomTextView);
			int fontEnumVal = TextFont.REG.value;

			for (int i = 0; i < a.getIndexCount(); i++) {
				if (a.getIndex(i) == R.styleable.CustomTextView_font) {
					fontEnumVal = a.getInt(R.styleable.CustomTextView_font, TextFont.REG.value);
				}
			}

            String fontName = "fonts/" + TextFont.get(fontEnumVal).getName() + ".ttf";

			if (!isInEditMode()) {
				Typeface myTypeface = Typeface.createFromAsset(getContext().getAssets(), fontName);
				setTypeface(myTypeface);
			}

			a.recycle();
		}
	}

	public void setTypeface(TextFont typeFace) {
        String fontName = "reg";
        if (typeFace != null) {
            fontName = typeFace.getName();
        }

        Typeface myTypeface = Typeface.createFromAsset(getContext().getAssets(), "fonts/" + fontName + ".ttf");
        setTypeface(myTypeface);
    }

}
import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import com.fortysevendeg.swipelistview.SwipeListView;
import com.mv2studio.amarok.kontrol.communication.CommandCallback;
import com.mv2studio.amarok.kontrol.communication.ConnectionMessage;
import com.mv2studio.amarok.kontrol.communication.ConnectorUpdateCallback;
import com.mv2studio.amarok.kontrol.communication.amarok.AmarokCommand;
import com.mv2studio.amarok.kontrol.helpers.BitmapConcurrencyHandler;
import com.mv2studio.amarok.kontrol.helpers.MediaHelper;
import com.mv2studio.amarok.kontrol.shared.PlayingState;
import com.mv2studio.amarok.kontrol.shared.model.Song;
import com.mv2studio.amarok.kontrol.ui.Activity.BaseActivity;
import com.mv2studio.amarok.kontrol.ui.View.CustomTextView;
import java.util.ArrayList;
import java.util.Collection;
		return 2;		
	}
	
	@Override
	public void clear() {
		mData.clear();
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public void addAll(Collection<? extends Song> collection) {
		mData = (ArrayList<Song>) collection;
		try{ mData.get(0).setNewAlbum(true); } catch(IndexOutOfBoundsException ex) {  return; }
		for(int songIndex = 1; songIndex < collection.size(); songIndex++) {
			if(!mData.get(songIndex).onSameAlbum(mData.get(songIndex-1))) {
				mData.get(songIndex).setNewAlbum(true);
			}
		}
        notifyDataSetChanged();
	}
	
	@Override
	public View getView(final int position, View convertView, final ViewGroup parent) {
		ViewHolder holder;
		final Song song = getItem(position);

		if(convertView == null) {
			holder = new ViewHolder();
			int type = getItemViewType(position);
			
			switch(type) {
			case ALBUM:
				convertView = mInflater.inflate(R.layout.item_playlist_album, parent, false);
				holder.cover = (ImageView) convertView.findViewById(R.id.listSongCover);				
				holder.artist = (CustomTextView) convertView.findViewById(R.id.listSongArtist);
				holder.album = (CustomTextView) convertView.findViewById(R.id.listSongAlbum);
				holder.downloadText = (CustomTextView) convertView.findViewById(R.id.playlist_back_view_download_text);
				holder.removeText = (CustomTextView) convertView.findViewById(R.id.playlist_back_view_remove_text);

                holder.download = convertView.findViewById(R.id.playlist_back_view_download);
				holder.remove = convertView.findViewById(R.id.playlist_back_view_remove);
				break;
			case SONG:
				convertView = mInflater.inflate(R.layout.item_playlist_song, parent, false);
				holder.download = convertView.findViewById(R.id.playlist_back_view_download);
				holder.remove = convertView.findViewById(R.id.playlist_back_view_remove);
				break;
			}
			holder.backLayout = (LinearLayout) convertView.findViewById(R.id.back);
			holder.frontLayout = (RelativeLayout) convertView.findViewById(R.id.front);


            holder.playingIcon = (ImageView) convertView.findViewById(R.id.listSongPlayingIcon);
			convertView.setTag(holder);
			holder.title = (CustomTextView) convertView.findViewById(R.id.listSongTitle);
		} else {
			holder = (ViewHolder) convertView.getTag();
		}
		
		 ((SwipeListView)parent).recycle(convertView, position);
		 ((SwipeListView)parent).closeOpenedItems();
		
		holder.frontLayout.setBackgroundColor(Color.parseColor((position % 2 == 0) ? "#77000000" : "#aa000000"));
		holder.frontLayout.setX(0f);

        holder.title.setTag(getItem(position).toString()); //TODO:null pointer

        if(getItemViewType(position) == ALBUM) {
            holder.song = song;
            holder.cover.setTag(position);

            mBch.loadBitmap(song.getArtist() + song.getAlbum() + "_mini", holder.cover);

            holder.artist.setText(song.getArtist());
            holder.album.setText(song.getAlbum());
        }

        holder.download.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                ((SwipeListView) parent).closeOpenedItems();
                try { MediaHelper.downloadSong(song.getId(), song, mBaseActivity); } catch(Exception e) {}
            }
        });

        holder.remove.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                ((SwipeListView) parent).closeOpenedItems();

                App.getInstance().sendCommand(
                        AmarokCommand.REMOVE_BY_INDEX.param(song.getId() + "").withCallback(
                                new CommandCallback() {
                                    @Override
                                    public void onResponse() {
                                        mBaseActivity.refreshPlaylist();
                                    }
                                }));
            }
        });

        holder.backLayout.setVisibility(View.INVISIBLE);
        holder.playingIcon.setVisibility((song.equals(currentSong)) ? View.VISIBLE : View.GONE);
        holder.title.setText(song.getTitle());

		return convertView;
	}
	
	public class ViewHolder {
		public ImageView cover, playingIcon, addedSongIcon, addedAlbumIcon;
		public CustomTextView artist, album, title, removeText, downloadText;
		public Song song;
		public int position;
		public Button albumHigh;
		public Button songHigh;
		public View remove, download;
		public RelativeLayout frontLayout;
		public LinearLayout backLayout;
	}

    private final ConnectorUpdateCallback mConnectorUpdateCallback = new ConnectorUpdateCallback() {