br.com.carlosrafaelgn.fplay.ActivityBrowserRadio.java Source code

Java tutorial

Introduction

Here is the source code for br.com.carlosrafaelgn.fplay.ActivityBrowserRadio.java

Source

//
// FPlayAndroid is distributed under the FreeBSD License
//
// Copyright (c) 2013-2014, Carlos Rafael Gimenes das Neves
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those
// of the authors and should not be interpreted as representing official policies,
// either expressed or implied, of the FreeBSD Project.
//
// https://github.com/carlosrafaelgn/FPlayAndroid
//
package br.com.carlosrafaelgn.fplay;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.ColorStateList;
import android.database.DataSetObserver;
import android.net.http.AndroidHttpClient;
import android.text.InputType;
import android.text.method.LinkMovementMethod;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RelativeLayout;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;
import android.widget.TextView;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.HttpGet;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;

import br.com.carlosrafaelgn.fplay.activity.MainHandler;
import br.com.carlosrafaelgn.fplay.list.FileSt;
import br.com.carlosrafaelgn.fplay.list.RadioStation;
import br.com.carlosrafaelgn.fplay.list.RadioStationList;
import br.com.carlosrafaelgn.fplay.playback.Player;
import br.com.carlosrafaelgn.fplay.ui.BgButton;
import br.com.carlosrafaelgn.fplay.ui.BgColorStateList;
import br.com.carlosrafaelgn.fplay.ui.BgListView;
import br.com.carlosrafaelgn.fplay.ui.RadioStationView;
import br.com.carlosrafaelgn.fplay.ui.SongAddingMonitor;
import br.com.carlosrafaelgn.fplay.ui.UI;
import br.com.carlosrafaelgn.fplay.ui.drawable.ColorDrawable;
import br.com.carlosrafaelgn.fplay.ui.drawable.TextIconDrawable;
import br.com.carlosrafaelgn.fplay.util.SafeURLSpan;

public final class ActivityBrowserRadio extends ActivityBrowserView
        implements View.OnClickListener, DialogInterface.OnClickListener, DialogInterface.OnCancelListener,
        BgListView.OnBgListViewKeyDownObserver, SpinnerAdapter {
    private TextView sep2;
    private BgListView list;
    private RadioStationList radioStationList;
    private RelativeLayout panelSecondary, panelLoading;
    private RadioButton chkGenre, chkTerm;
    private ColorStateList defaultTextColors;
    private Spinner btnGenre;
    private EditText txtTerm;
    private BgButton btnGoBack, btnFavorite, btnSearch, btnGoBackToPlayer, btnAdd, btnPlay;
    private boolean loading, isAtFavorites;

    @Override
    public CharSequence getTitle() {
        return getText(R.string.add_radio);
    }

    private void updateButtons() {
        if (!isAtFavorites != (btnFavorite.getVisibility() == View.VISIBLE)) {
            if (isAtFavorites) {
                btnFavorite.setVisibility(View.GONE);
                btnSearch.setVisibility(View.GONE);
                btnGoBack.setNextFocusRightId(R.id.list);
                UI.setNextFocusForwardId(btnGoBack, R.id.list);
            } else {
                btnFavorite.setVisibility(View.VISIBLE);
                btnSearch.setVisibility(View.VISIBLE);
                btnGoBack.setNextFocusRightId(R.id.btnFavorite);
                UI.setNextFocusForwardId(btnGoBack, R.id.btnFavorite);
            }
        }
        final int s = radioStationList.getSelection();
        if ((s >= 0) != (btnAdd.getVisibility() == View.VISIBLE)) {
            if (s >= 0) {
                btnAdd.setVisibility(View.VISIBLE);
                sep2.setVisibility(View.VISIBLE);
                btnPlay.setVisibility(View.VISIBLE);
                btnGoBack.setNextFocusLeftId(R.id.btnPlay);
                btnGoBackToPlayer.setNextFocusRightId(R.id.btnAdd);
                UI.setNextFocusForwardId(btnGoBackToPlayer, R.id.btnAdd);
            } else {
                btnAdd.setVisibility(View.GONE);
                sep2.setVisibility(View.GONE);
                btnPlay.setVisibility(View.GONE);
                btnGoBack.setNextFocusLeftId(R.id.btnGoBackToPlayer);
                btnGoBackToPlayer.setNextFocusRightId(R.id.btnGoBack);
                UI.setNextFocusForwardId(btnGoBackToPlayer, R.id.btnGoBack);
            }
        }
    }

    private void addPlaySelectedItem(final boolean play) {
        if (radioStationList.getSelection() < 0)
            return;
        try {
            final RadioStation radioStation = radioStationList.getItemT(radioStationList.getSelection());
            if (radioStation.m3uUri == null || radioStation.m3uUri.length() < 0) {
                UI.toast(getApplication(), R.string.error_file_not_found);
                return;
            }
            Player.songs.addingStarted();
            SongAddingMonitor.start(getHostActivity());
            (new Thread("Checked Radio Station Adder Thread") {
                @Override
                public void run() {
                    AndroidHttpClient client = null;
                    HttpResponse response = null;
                    InputStream is = null;
                    InputStreamReader isr = null;
                    BufferedReader br = null;
                    try {
                        if (Player.getState() == Player.STATE_TERMINATED
                                || Player.getState() == Player.STATE_TERMINATING) {
                            Player.songs.addingEnded();
                            return;
                        }
                        client = AndroidHttpClient.newInstance(
                                "Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36 Debian");
                        response = client.execute(new HttpGet(radioStation.m3uUri));
                        final StatusLine statusLine = response.getStatusLine();
                        final int s = statusLine.getStatusCode();
                        if (s == HttpStatus.SC_OK) {
                            is = response.getEntity().getContent();
                            isr = new InputStreamReader(is, "UTF-8");
                            br = new BufferedReader(isr, 1024);
                            ArrayList<String> lines = new ArrayList<String>(8);
                            String line;
                            while ((line = br.readLine()) != null) {
                                line = line.trim();
                                if (line.length() > 0 && line.charAt(0) != '#'
                                        && (line.regionMatches(true, 0, "http://", 0, 7)
                                                || line.regionMatches(true, 0, "https://", 0, 8)))
                                    lines.add(line);
                            }
                            if (Player.getState() == Player.STATE_TERMINATED
                                    || Player.getState() == Player.STATE_TERMINATING) {
                                Player.songs.addingEnded();
                                return;
                            }
                            if (lines.size() == 0) {
                                Player.songs.addingEnded();
                                MainHandler.toast(R.string.error_gen);
                            } else {
                                //instead of just using the first available address, let's use
                                //one from the middle ;)
                                Player.songs.addFiles(new FileSt[] {
                                        new FileSt(lines.get(lines.size() >> 1), radioStation.title, null, 0) },
                                        null, 1, play, false, true);
                            }
                        } else {
                            Player.songs.addingEnded();
                            MainHandler.toast(
                                    (s >= 400 && s < 500) ? R.string.error_file_not_found : R.string.error_gen);
                        }
                    } catch (Throwable ex) {
                        Player.songs.addingEnded();
                        MainHandler.toast(ex);
                    } finally {
                        try {
                            if (client != null)
                                client.close();
                        } catch (Throwable ex) {
                        }
                        try {
                            if (is != null)
                                is.close();
                        } catch (Throwable ex) {
                        }
                        try {
                            if (isr != null)
                                isr.close();
                        } catch (Throwable ex) {
                        }
                        try {
                            if (br != null)
                                br.close();
                        } catch (Throwable ex) {
                        }
                        br = null;
                        isr = null;
                        is = null;
                        client = null;
                        response = null;
                        System.gc();
                    }
                }
            }).start();
        } catch (Throwable ex) {
            Player.songs.addingEnded();
            UI.toast(getApplication(), ex.getMessage());
        }
    }

    @Override
    public void loadingProcessChanged(boolean started) {
        if (UI.browserActivity != this)
            return;
        loading = started;
        if (panelLoading != null)
            panelLoading.setVisibility(started ? View.VISIBLE : View.GONE);
        if (list != null)
            list.setCustomEmptyText(getText(
                    started ? R.string.loading : (isAtFavorites ? R.string.no_favorites : R.string.no_stations)));
        if (!started)
            updateButtons();
    }

    @Override
    public View createView() {
        return new RadioStationView(Player.getService());
    }

    @Override
    public void processItemButtonClick(int position, boolean add) {
        final RadioStation station = radioStationList.getItemT(position);
        if (station.isFavorite)
            radioStationList.addFavoriteStation(station);
        else
            radioStationList.removeFavoriteStation(station);
    }

    @Override
    public void processItemClick(int position) {
        //UI.doubleClickMode is ignored for radio stations!
        if (radioStationList.getSelection() == position) {
            addPlaySelectedItem(true);
        } else {
            radioStationList.setSelection(position, true);
            updateButtons();
        }
    }

    @Override
    public void processItemLongClick(int position) {
        if (radioStationList.getSelection() != position) {
            radioStationList.setSelection(position, true);
            updateButtons();
        }
    }

    private static int getValidGenre(int genre) {
        return ((genre < 0) ? 0
                : ((genre >= RadioStationList.GENRES.length) ? (RadioStationList.GENRES.length - 1) : genre));
    }

    private static String getGenreString(int genre) {
        return RadioStationList.GENRES[getValidGenre(genre)];
    }

    private void doSearch() {
        if (Player.radioSearchTerm != null && Player.radioSearchTerm.length() < 1)
            Player.radioSearchTerm = null;
        if (Player.lastRadioSearchWasByGenre || Player.radioSearchTerm == null)
            radioStationList.fetchIcecast(getApplication(), getGenreString(Player.radioLastGenre), null);
        else
            radioStationList.fetchIcecast(getApplication(), null, Player.radioSearchTerm);
        updateButtons();
    }

    @Override
    public boolean onBgListViewKeyDown(BgListView bgListView, int keyCode, KeyEvent event) {
        int p;
        switch (keyCode) {
        case KeyEvent.KEYCODE_DPAD_LEFT:
            if (btnSearch.getVisibility() == View.VISIBLE)
                btnSearch.requestFocus();
            else
                btnGoBack.requestFocus();
            return true;
        case KeyEvent.KEYCODE_DPAD_RIGHT:
            btnGoBackToPlayer.requestFocus();
            return true;
        case KeyEvent.KEYCODE_ENTER:
        case KeyEvent.KEYCODE_DPAD_CENTER:
        case KeyEvent.KEYCODE_0:
        case KeyEvent.KEYCODE_SPACE:
            p = radioStationList.getSelection();
            if (p >= 0)
                processItemClick(p);
            return true;
        }
        return false;
    }

    @Override
    public void onClick(View view) {
        if (view == btnGoBack) {
            if (isAtFavorites) {
                isAtFavorites = false;
                doSearch();
            } else {
                finish(0, view, true);
            }
        } else if (view == btnFavorite) {
            isAtFavorites = true;
            radioStationList.cancel();
            radioStationList.fetchFavorites(getApplication());
            updateButtons();
        } else if (view == btnSearch) {
            final Context ctx = getHostActivity();
            final LinearLayout l = (LinearLayout) UI.createDialogView(ctx, null);

            LinearLayout.LayoutParams p = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
                    LinearLayout.LayoutParams.WRAP_CONTENT);
            chkGenre = new RadioButton(ctx);
            chkGenre.setText(R.string.genre);
            chkGenre.setChecked(Player.lastRadioSearchWasByGenre);
            chkGenre.setOnClickListener(this);
            chkGenre.setTextSize(TypedValue.COMPLEX_UNIT_PX, UI._DLGsp);
            chkGenre.setLayoutParams(p);

            p = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
                    LinearLayout.LayoutParams.WRAP_CONTENT);
            p.topMargin = UI._DLGsppad;
            btnGenre = new Spinner(ctx);
            btnGenre.setContentDescription(ctx.getText(R.string.genre));
            btnGenre.setLayoutParams(p);

            p = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
                    LinearLayout.LayoutParams.WRAP_CONTENT);
            p.topMargin = UI._DLGsppad << 1;
            chkTerm = new RadioButton(ctx);
            chkTerm.setText(R.string.search_term);
            chkTerm.setChecked(!Player.lastRadioSearchWasByGenre);
            chkTerm.setOnClickListener(this);
            chkTerm.setTextSize(TypedValue.COMPLEX_UNIT_PX, UI._DLGsp);
            chkTerm.setLayoutParams(p);

            p = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
                    LinearLayout.LayoutParams.WRAP_CONTENT);
            p.topMargin = UI._DLGsppad;
            txtTerm = new EditText(ctx);
            txtTerm.setContentDescription(ctx.getText(R.string.search_term));
            txtTerm.setText(Player.radioSearchTerm == null ? "" : Player.radioSearchTerm);
            txtTerm.setOnClickListener(this);
            txtTerm.setTextSize(TypedValue.COMPLEX_UNIT_PX, UI._DLGsp);
            txtTerm.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES);
            txtTerm.setSingleLine();
            txtTerm.setLayoutParams(p);

            p = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
                    LinearLayout.LayoutParams.WRAP_CONTENT);
            p.topMargin = UI._DLGsppad;
            p.bottomMargin = UI._DLGsppad;
            final TextView lbl = new TextView(ctx);
            lbl.setAutoLinkMask(0);
            lbl.setLinksClickable(true);
            //http://developer.android.com/design/style/color.html
            lbl.setLinkTextColor(new BgColorStateList(UI.isAndroidThemeLight() ? 0xff0099cc : 0xff33b5e5));
            lbl.setTextSize(TypedValue.COMPLEX_UNIT_PX, UI._14sp);
            lbl.setGravity(Gravity.CENTER_HORIZONTAL);
            lbl.setText(SafeURLSpan.parseSafeHtml(getText(R.string.by_dir_xiph_org)));
            lbl.setMovementMethod(LinkMovementMethod.getInstance());
            lbl.setLayoutParams(p);

            l.addView(chkGenre);
            l.addView(btnGenre);
            l.addView(chkTerm);
            l.addView(txtTerm);
            l.addView(lbl);

            btnGenre.setAdapter(this);
            btnGenre.setSelection(getValidGenre(Player.radioLastGenre));
            defaultTextColors = txtTerm.getTextColors();

            UI.prepareDialogAndShow((new AlertDialog.Builder(ctx)).setTitle(getText(R.string.search)).setView(l)
                    .setPositiveButton(R.string.search, this).setNegativeButton(R.string.cancel, this)
                    .setOnCancelListener(this).create());
        } else if (view == btnGoBackToPlayer) {
            finish(-1, view, false);
        } else if (view == btnAdd) {
            addPlaySelectedItem(false);
        } else if (view == btnPlay) {
            addPlaySelectedItem(true);
        } else if (view == chkGenre || view == btnGenre) {
            chkGenre.setChecked(true);
            chkTerm.setChecked(false);
        } else if (view == chkTerm || view == txtTerm) {
            chkGenre.setChecked(false);
            chkTerm.setChecked(true);
        } else if (view == list) {
            if (!isAtFavorites && !loading && (radioStationList == null || radioStationList.getCount() == 0))
                onClick(btnFavorite);
        }
    }

    @Override
    public void onClick(DialogInterface dialog, int which) {
        if (which == AlertDialog.BUTTON_POSITIVE) {
            Player.lastRadioSearchWasByGenre = chkGenre.isChecked();
            Player.radioLastGenre = btnGenre.getSelectedItemPosition();
            Player.radioSearchTerm = txtTerm.getText().toString().trim();
            doSearch();
        }
        chkGenre = null;
        btnGenre = null;
        chkTerm = null;
        txtTerm = null;
        defaultTextColors = null;
    }

    @Override
    public void onCancel(DialogInterface dialog) {
        onClick(dialog, AlertDialog.BUTTON_NEGATIVE);
    }

    @Override
    protected boolean onBackPressed() {
        if (UI.backKeyAlwaysReturnsToPlayerWhenBrowsing) {
            finish(-1, null, false);
            return true;
        }
        if (!isAtFavorites)
            return false;
        onClick(btnGoBack);
        return true;
    }

    @Override
    protected void onCreate() {
        UI.browserActivity = this;
        radioStationList = new RadioStationList(getText(R.string.tags).toString(), "-",
                getText(R.string.no_description).toString(), getText(R.string.no_tags).toString());
    }

    @SuppressWarnings("deprecation")
    @Override
    protected void onCreateLayout(boolean firstCreation) {
        setContentView(R.layout.activity_browser_radio);
        UI.smallTextAndColor((TextView) findViewById(R.id.lblLoading));
        list = (BgListView) findViewById(R.id.list);
        list.setOnKeyDownObserver(this);
        list.setScrollBarType((UI.browserScrollBarType == BgListView.SCROLLBAR_INDEXED) ? BgListView.SCROLLBAR_LARGE
                : UI.browserScrollBarType);
        list.setCustomEmptyText(getText(R.string.loading));
        list.setEmptyListOnClickListener(this);
        radioStationList.setObserver(list);
        panelLoading = (RelativeLayout) findViewById(R.id.panelLoading);
        btnGoBack = (BgButton) findViewById(R.id.btnGoBack);
        btnGoBack.setOnClickListener(this);
        btnGoBack.setIcon(UI.ICON_GOBACK);
        btnFavorite = (BgButton) findViewById(R.id.btnFavorite);
        btnFavorite.setOnClickListener(this);
        btnFavorite.setIcon(UI.ICON_FAVORITE_ON);
        btnSearch = (BgButton) findViewById(R.id.btnSearch);
        btnSearch.setOnClickListener(this);
        btnSearch.setIcon(UI.ICON_SEARCH);
        panelSecondary = (RelativeLayout) findViewById(R.id.panelSecondary);
        btnGoBackToPlayer = (BgButton) findViewById(R.id.btnGoBackToPlayer);
        btnGoBackToPlayer.setTextColor(UI.colorState_text_reactive);
        btnGoBackToPlayer.setOnClickListener(this);
        btnGoBackToPlayer.setCompoundDrawables(
                new TextIconDrawable(UI.ICON_RIGHT, UI.color_text, UI.defaultControlContentsSize), null, null,
                null);
        btnGoBackToPlayer.setDefaultHeight();
        btnAdd = (BgButton) findViewById(R.id.btnAdd);
        btnAdd.setTextColor(UI.colorState_text_reactive);
        btnAdd.setOnClickListener(this);
        btnAdd.setIcon(UI.ICON_ADD, true, false);
        sep2 = (TextView) findViewById(R.id.sep2);
        RelativeLayout.LayoutParams rp = new RelativeLayout.LayoutParams(UI.strokeSize,
                UI.defaultControlContentsSize);
        rp.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);
        rp.addRule(RelativeLayout.LEFT_OF, R.id.btnPlay);
        rp.leftMargin = UI._8dp;
        rp.rightMargin = UI._8dp;
        sep2.setLayoutParams(rp);
        sep2.setBackgroundDrawable(new ColorDrawable(UI.color_highlight));
        btnPlay = (BgButton) findViewById(R.id.btnPlay);
        btnPlay.setTextColor(UI.colorState_text_reactive);
        btnPlay.setOnClickListener(this);
        btnPlay.setIcon(UI.ICON_PLAY, true, false);
        UI.prepareControlContainer(findViewById(R.id.panelControls), false, true);
        UI.prepareControlContainer(panelSecondary, true, false);
        if (UI.isLargeScreen)
            UI.prepareViewPaddingForLargeScreen(list, 0, 0);
        UI.prepareEdgeEffectColor(getApplication());
        updateButtons();
        doSearch();
    }

    @Override
    protected void onPause() {
        SongAddingMonitor.stop();
        radioStationList.saveFavorites(getApplication());
        radioStationList.setObserver(null);
    }

    @Override
    protected void onResume() {
        UI.browserActivity = this;
        radioStationList.setObserver(list);
        SongAddingMonitor.start(getHostActivity());
        if (loading != radioStationList.isLoading())
            loadingProcessChanged(radioStationList.isLoading());
    }

    @Override
    protected void onOrientationChanged() {
        if (UI.isLargeScreen && list != null)
            UI.prepareViewPaddingForLargeScreen(list, 0, 0);
    }

    @Override
    protected void onCleanupLayout() {
        list = null;
        panelLoading = null;
        panelSecondary = null;
        btnGoBack = null;
        btnFavorite = null;
        btnSearch = null;
        btnGoBackToPlayer = null;
        btnAdd = null;
        sep2 = null;
        btnPlay = null;
    }

    @Override
    protected void onDestroy() {
        UI.browserActivity = null;
        radioStationList.cancel();
        radioStationList = null;
    }

    @Override
    public int getCount() {
        return RadioStationList.GENRES.length;
    }

    @Override
    public Object getItem(int position) {
        return getGenreString(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public int getItemViewType(int position) {
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        TextView txt = (TextView) convertView;
        if (txt == null) {
            txt = new TextView(getApplication());
            txt.setPadding(UI._8dp, UI._4dp, UI._8dp, UI._4dp);
            txt.setTypeface(UI.defaultTypeface);
            txt.setTextSize(TypedValue.COMPLEX_UNIT_PX, UI._DLGsp);
            txt.setTextColor(defaultTextColors);
        }
        txt.setText(getGenreString(position));
        return txt;
    }

    @Override
    public int getViewTypeCount() {
        return 1;
    }

    @Override
    public boolean hasStableIds() {
        return true;
    }

    @Override
    public boolean isEmpty() {
        return false;
    }

    @Override
    public void registerDataSetObserver(DataSetObserver observer) {
    }

    @Override
    public void unregisterDataSetObserver(DataSetObserver observer) {
    }

    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent) {
        TextView txt = (TextView) convertView;
        if (txt == null) {
            txt = new TextView(getApplication());
            txt.setPadding(UI._DLGdppad, UI._DLGsppad, UI._DLGdppad, UI._DLGsppad);
            txt.setTypeface(UI.defaultTypeface);
            txt.setTextSize(TypedValue.COMPLEX_UNIT_PX, UI._DLGsp);
            txt.setTextColor(defaultTextColors);
        }
        txt.setText(getGenreString(position));
        return txt;
    }
}