package com.macrohard.musicbug; import java.util.ArrayList; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import com.macrohard.musicbug.R; import com.macrohard.musicbug.IMp3Fetcher.Mp3FetcherException; import android.app.Activity; import android.app.Dialog; import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import android.widget.AdapterView.OnItemClickListener; public class Mp3ListActivity extends Activity implements ListFooterView.RetryNetworkInterface { private static final String TAG = Debug.TAG; private static final int DIALOG_WAITING_FOR_SERVER = 1; // Shared by multiple threads. private volatile Mp3ListWrapper mData; private ProgressDialog mProgressDialog; private ListView mListView; private Mp3ListAdapter mAdapter; private boolean mHasMoreData = true; private IMp3Fetcher mFetcher; @Override protected Dialog onCreateDialog(int id) { switch (id) { case DIALOG_WAITING_FOR_SERVER: { mProgressDialog = new ProgressDialog(this); mProgressDialog.setMessage(getString(R.string.waiting_for_server)); mProgressDialog.setIndeterminate(true); mProgressDialog.setCancelable(true); return mProgressDialog; } } return null; } @Override public void retryNetwork() { fetchNextMp3ListBatch(); mAdapter.setStatus(ListFooterView.Status.LOADING); mAdapter.notifyDataSetChanged(); } private void fetchNextMp3ListBatch() { if (mFetcher != null) { new FetchMp3ListTask().execute(); } } @Override public void onResume() { super.onResume(); showDialog(DIALOG_WAITING_FOR_SERVER); fetchNextMp3ListBatch(); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Debug.D("Mp3ListActivity onCreate()"); setContentView(R.layout.music_list); Ads.addAds(this); mListView = (ListView)findViewById(R.id.post_list); mListView.setTextFilterEnabled(true); mListView.setFocusable(true); mListView.setItemsCanFocus(true); mData = new Mp3ListWrapper(); mAdapter = new Mp3ListAdapter( Mp3ListActivity.this, R.layout.music_item ); mListView.setAdapter(mAdapter); mListView.setSelection(0); mListView.setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View v, int position, long id) { if (mData == null || position == mData.size()) return; final MP3Info mp3 = mData.get(position); showDialog(DIALOG_WAITING_FOR_SERVER); new FetchMp3LinkTask().execute(mp3); } }); String keyWords = getIntent().getStringExtra(Const.Key); if (!TextUtils.isEmpty(keyWords)) { mFetcher = new SogoMp3Fetcher(this, keyWords); } } private class Mp3InfoHolder { public MP3Info mp3; public String downloadLink; }; private class FetchMp3LinkTask extends AsyncTask<MP3Info, Void, Mp3InfoHolder> { protected Mp3InfoHolder doInBackground(MP3Info... mp3s) { String mp3Link = null; try { mp3Link = mFetcher.getDownloadLink(mp3s[0]); } catch (Mp3FetcherException e) { mp3Link = null; } Mp3InfoHolder holder = new Mp3InfoHolder(); holder.mp3 = mp3s[0]; holder.downloadLink = mp3Link; return holder; } protected void onPostExecute(Mp3InfoHolder info) { MP3Info mp3 = info.mp3; String mp3Link = info.downloadLink; if (mp3Link == null) { mProgressDialog.dismiss(); Toast.makeText(Mp3ListActivity.this, R.string.no_result, Toast.LENGTH_SHORT).show(); return; } float rate = mp3.getRate(); Intent intent = new Intent(Mp3ListActivity.this, MusicPage.class); intent.putExtra(Const.MP3RATE, ((Float)rate).toString()); intent.putExtra(Const.MP3LOC, mp3Link); intent.putExtra(Const.MP3TITLE, mp3.getTitle()); intent.putExtra(Const.MP3SINGER, mp3.getArtist()); startActivity(intent); mProgressDialog.dismiss(); } } private class FetchMp3ListTask extends AsyncTask<Void, Void, Boolean> { @Override protected void onPostExecute(Boolean result) { mProgressDialog.dismiss(); if (result) { mAdapter.setStatus(ListFooterView.Status.LOADED); mAdapter.notifyDataSetChanged(); } else { mAdapter.setStatus(ListFooterView.Status.ERROR); mAdapter.notifyDataSetInvalidated(); } } @Override protected Boolean doInBackground(Void... params) { ArrayList<MP3Info> mp3List; try { mp3List = mFetcher.getNextListBatch(); if (mp3List == null) { // Some error. return false; } else { if (mp3List.size() > 0) { mData.append(mp3List); return true; } else { mHasMoreData = false; return true; } } } catch (Mp3FetcherException e) { e.printStackTrace(); return false; } } } // A thread safe wrapper around ArrayList<MP3Info>. private final class Mp3ListWrapper { private ArrayList<MP3Info> mMp3List; private ReadWriteLock lock = new ReentrantReadWriteLock(); private Lock r = lock.readLock(); private Lock w = lock.writeLock(); public Mp3ListWrapper() { mMp3List = new ArrayList<MP3Info>(); } public void add(MP3Info info) { w.lock(); try { mMp3List.add(info); } finally { w.unlock(); } } public void append(ArrayList<MP3Info> mp3List) { w.lock(); try { for (int i = 0; i < mp3List.size(); ++i) { add(mp3List.get(i)); } } finally { w.unlock(); } } public int size() { r.lock(); try { return mMp3List.size(); } finally { r.unlock(); } } public MP3Info get(int i) { r.lock(); try { return mMp3List.get(i); } finally { r.unlock(); } } public void remove(int i) { r.lock(); try { mMp3List.remove(i); } finally { r.unlock(); } } } private final class Mp3ListAdapter extends BaseAdapter { private final static int VIEW_TYPE_NORMAL = 0; private final static int VIEW_TYPE_FOOTER = 1; private int mResource; private LayoutInflater mInflater; private ListFooterView.Status mStatus; public Mp3ListAdapter(Context context, int resource) { mResource = resource; mInflater = (LayoutInflater)context.getSystemService( Context.LAYOUT_INFLATER_SERVICE); mStatus = ListFooterView.Status.LOADED; } public void setStatus(ListFooterView.Status status) { mStatus = status; } @Override public int getCount() { boolean showFooter = mStatus == ListFooterView.Status.ERROR || mStatus == ListFooterView.Status.LOADING; int footerCount = showFooter ? 1 : 0; if (mData == null) return footerCount; return mData.size() + footerCount; } @Override public Object getItem(int position) { if (mData == null) return null; if (position < mData.size()) return mData.get(position); return null; // footer. } @Override public long getItemId(int position) { if (mData == null) return -1; if (position < mData.size()) return position; return -1; // footer. } @Override public int getViewTypeCount() { return 2; } @Override public int getItemViewType(int position) { if (position == mData.size()) { return VIEW_TYPE_FOOTER; } return VIEW_TYPE_NORMAL; } @Override public View getView(int position, View convertView, ViewGroup parent) { boolean isFooter = position == mData.size(); if (isFooter) { ListFooterView footerView = (ListFooterView)convertView; if (footerView == null) { footerView = (ListFooterView)mInflater.inflate( R.layout.list_footer, null); TextView text = (TextView)footerView.findViewById(R.id.footer_text); text.setText(R.string.loading); } footerView.bind(mStatus, Mp3ListActivity.this); return footerView; } View v; Object item = mData.get(position); if (convertView == null) { v = mInflater.inflate(mResource, parent, false); } else { v = convertView; } v.setBackgroundResource( position % 2 == 0 ? R.color.mp3_item_color : R.color.mp3_item_color2); MP3Info info = (MP3Info)item; ((TextView)v.findViewById(R.id.title)).setText(info.getTitle()); ((TextView)v.findViewById(R.id.artist)).setText(info.getArtist()); if (mHasMoreData && position == mData.size() - 1 && mStatus == ListFooterView.Status.LOADED) { Debug.D("Loading more"); fetchNextMp3ListBatch(); setStatus(ListFooterView.Status.LOADING); notifyDataSetChanged(); } return v; } } public static void handleMp3ListIntent(Context context, String keyWords) { Intent intent = new Intent(context, Mp3ListActivity.class); intent.putExtra(Const.Key, keyWords); context.startActivity(intent); } }