package com.ringtone.music; import java.io.IOException; import java.util.ArrayList; import java.util.Hashtable; import java.util.Iterator; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import com.admob.android.ads.AdView; import com.ringtone.music.download.DownloadActivity; import com.ringtone.music.download.DownloadInfo; import com.ringtone.music.download.DownloadService; import com.ringtone.music.R; import android.app.AlertDialog; import android.app.Dialog; import android.app.ListActivity; import android.app.ProgressDialog; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.ServiceConnection; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.View.OnClickListener; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; public class SearchResultActivity extends ListActivity { private static final String TAG = Utils.TAG; private static final int DIALOG_WAITING_FOR_SERVER = 1; private static final int DIALOG_MUSIC_OPTIONS = 2; private static final int DIALOG_MUSIC_STREAMING = 3; private static final int MUSIC_OPTION_PREVIEW = 0; private static final int MUSIC_OPTION_DOWNLOAD = 1; private static Mp3ListWrapper sData; private static String sQuery; private static FetchMp3ListTask sFetchMp3ListTask; private static SearchResultActivity sSearchActivity; private static volatile IMusicSearcher sFetcher; private static boolean sHasMoreData = true; private MusicInfo mCurrentMusic; @SuppressWarnings("unused") private SearchBar mSearch; private Handler mHandler = new Handler(); private ProgressDialog mProgressDialog; private ProgressBar mProgressBar; private TextView mSearchMessage; private Mp3ListAdapter mAdapter; private DownloadService mDownloadService; private ProgressDialog mStreaming; private static String sStreamingTitle; private static volatile MediaPlayer sPlayer; private static Thread sPreviewThread; private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { mDownloadService = ((DownloadService.LocalBinder) service).getService(); } public void onServiceDisconnected(ComponentName className) { mDownloadService = null; } }; @Override protected void onPrepareDialog(int id, Dialog dialog) { switch (id) { case DIALOG_MUSIC_OPTIONS: { if (mCurrentMusic != null) dialog.setTitle("Options for \"" + mCurrentMusic.getTitle() + "\""); return; } case DIALOG_MUSIC_STREAMING: { if (mCurrentMusic != null) { sStreamingTitle = mCurrentMusic.getTitle(); } if (!TextUtils.isEmpty(sStreamingTitle)) { dialog.setTitle(sStreamingTitle); } } } } @Override protected Dialog onCreateDialog(int id) { Utils.D("onCreateDialog() " + id); switch (id) { case DIALOG_WAITING_FOR_SERVER: { if (mProgressDialog == null) { mProgressDialog = new ProgressDialog(this); mProgressDialog.setMessage(getString(R.string.wait)); mProgressDialog.setIndeterminate(true); mProgressDialog.setCancelable(true); } return mProgressDialog; } case DIALOG_MUSIC_OPTIONS: return new AlertDialog.Builder(SearchResultActivity.this) .setTitle(R.string.options) .setItems(R.array.music_item_options, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { switch (which) { case MUSIC_OPTION_PREVIEW: if (mCurrentMusic == null) return; mHandler.post(new Runnable() { @Override public void run() { showDialog(DIALOG_MUSIC_STREAMING); } }); if (TextUtils.isEmpty(mCurrentMusic.getDownloadUrl())) { new FetchMp3LinkTaskForPreview().execute(mCurrentMusic); break; } playMusic(mCurrentMusic); break; case MUSIC_OPTION_DOWNLOAD: if (mCurrentMusic == null) return; download(mCurrentMusic); break; } } }) .create(); case DIALOG_MUSIC_STREAMING: if (mStreaming == null) { mStreaming = new ProgressDialog(SearchResultActivity.this); mStreaming.setTitle("Streaming music..."); mStreaming.setMessage(getString(R.string.wait_streaming)); mStreaming.setIndeterminate(true); mStreaming.setCancelable(false); mStreaming.setButton(getString(R.string.stop), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (mStreaming != null) { mStreaming.dismiss(); } new Thread(new Runnable() { @Override public void run() { MediaPlayer player = sPlayer; if (player != null) player.release(); sPlayer = null; } }).start(); } }); } return mStreaming; } return null; } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.getmore_menu, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.bill_dowloads: Intent intent = new Intent(SearchResultActivity.this, DownloadActivity.class); startActivity(intent); return true; case R.id.bill_getmore: String url1 = "market://search?q=pub:\"Social Games\""; try { Uri uri = Uri.parse(url1); Intent intent1 = new Intent(Intent.ACTION_VIEW, uri); intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent1); } catch (Exception ex) { ex.printStackTrace(); } return true; } return false; } private OnClickListener mRetryListener = new OnClickListener() { @Override public void onClick(View v) { fetchNextMp3ListBatch(getApplication()); mAdapter.setStatus(ListStatusView.Status.LOADING); mAdapter.notifyDataSetChanged(); } }; private static void fetchNextMp3ListBatch(Context context) { if (sFetchMp3ListTask != null) sFetchMp3ListTask.cancel(true); sFetchMp3ListTask = new FetchMp3ListTask(context); sFetchMp3ListTask.execute(); } @Override public void onResume() { super.onResume(); Utils.D("onResume"); // Hack if (mProgressDialog != null && mProgressDialog.isShowing()) mProgressDialog.dismiss(); // Display mAdapter = new Mp3ListAdapter( SearchResultActivity.this, R.layout.bill_item); setListAdapter(mAdapter); if (sData == null) { if (sFetchMp3ListTask != null) { if (!TextUtils.isEmpty(sQuery)) { mProgressBar.setVisibility(View.VISIBLE); mSearchMessage.setText("Please wait while we search \"" + sQuery + "\""); } } else { mProgressBar.setVisibility(View.GONE); } } if (sPlayer != null) { showDialog(DIALOG_MUSIC_STREAMING); } } public void notifyDataSetInvalidated() { if (mAdapter != null) mAdapter.notifyDataSetInvalidated(); } public static void startQuery(Context context, String keyWords) { if (!TextUtils.isEmpty(keyWords)) { sQuery = keyWords; sData = null; if (sSearchActivity != null) sSearchActivity.notifyDataSetInvalidated(); sHasMoreData = true; //sFetcher = MusicSearcherFactory.getInstance(MusicSearcherFactory.ID_MERGED); //sFetcher = MusicSearcherFactory.getInstance(MusicSearcherFactory.ID_BAIDU); sFetcher = MusicSearcherFactory.getInstance(MusicSearcherFactory.ID_SOGOU); //sFetcher = MusicSearcherFactory.getInstance(MusicSearcherFactory.ID_SKREEMR); //sFetcher = MusicSearcherFactory.getInstance(MusicSearcherFactory.ID_REALM); sFetcher.setQuery(keyWords); fetchNextMp3ListBatch(context); } else { sFetchMp3ListTask = null; sFetcher = null; } } @Override protected void onNewIntent(Intent intent) { Utils.D("onNewIntent"); if (mAdapter != null) mAdapter.notifyDataSetInvalidated(); } @Override protected void onListItemClick(ListView listView, View view, int position, long id) { if (sData != null && position < sData.size()) { mCurrentMusic = sData.get(position); showDialog(DIALOG_MUSIC_OPTIONS); } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); sSearchActivity = this; Utils.D("Mp3ListActivity onCreate()"); setContentView(R.layout.bill_list); Utils.addAds(this); bindService(new Intent(this, DownloadService.class), mConnection, Context.BIND_AUTO_CREATE); mSearchMessage = (TextView) findViewById(R.id.bill_search_message); mProgressBar = (ProgressBar) findViewById(R.id.bill_search_progress); // mSearch = new SearchBar(this); } private void onPlayError() { MediaPlayer player = sPlayer; sPlayer = null; if (player != null) player.release(); runOnUiThread(new Runnable() { @Override public void run() { if (mStreaming != null) { mStreaming.dismiss(); } Toast.makeText(getApplication(), "Streaming error", Toast.LENGTH_LONG).show(); } }); } private void playMusic(final MusicInfo mp3) { if (mp3.getDownloadUrl() !=null && mp3.getDownloadUrl().startsWith("http:")) { sPreviewThread = new Thread(new Runnable() { @Override public void run() { try { MediaPlayer player = sPlayer; if (player != null) { player.release(); } sPlayer = new MediaPlayer(); player = sPlayer; player.reset(); player.setDataSource(mp3.getDownloadUrl()); player.prepare(); player.start(); player.setOnCompletionListener(new OnCompletionListener () { @Override public void onCompletion(MediaPlayer mp) { sPlayer = null; runOnUiThread(new Runnable() { @Override public void run() { if (mStreaming != null) { mStreaming.dismiss(); } } }); } }); player.setOnErrorListener(new OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { onPlayError(); return true; } }); if (sPlayer == null) { // Someone requested us to stop. player.release(); } } catch (IllegalArgumentException e) { onPlayError(); e.printStackTrace(); } catch (IllegalStateException e) { //onPlayError(); e.printStackTrace(); } catch (IOException e) { onPlayError(); e.printStackTrace(); } finally { sPreviewThread = null; } } }); sPreviewThread.start(); } } private void download(MusicInfo mp3) { if (TextUtils.isEmpty(mp3.getDownloadUrl())) { showDialog(DIALOG_WAITING_FOR_SERVER); new FetchMp3LinkTaskForDownload().execute(mp3); } else { DownloadInfo download = new DownloadInfo(mp3.getDownloadUrl(), MusicInfo.downloadPath(mp3)); mDownloadService.insertDownload(download); Intent intent = new Intent(SearchResultActivity.this, DownloadActivity.class); startActivity(intent); } } @Override protected void onDestroy() { super.onDestroy(); Utils.D("onDestroy()"); if (mProgressDialog != null && mProgressDialog.isShowing()) { mProgressDialog.dismiss(); } mProgressDialog = null; unbindService(mConnection); sSearchActivity = null; } private class FetchMp3LinkTaskForDownload extends AsyncTask<MusicInfo, Void, MusicInfo> { protected MusicInfo doInBackground(MusicInfo... mp3s) { MusicInfo mp3 = mp3s[0]; sFetcher.setMusicDownloadUrl(SearchResultActivity.this, mp3); return mp3; } protected void onPostExecute(MusicInfo mp3) { if (mp3.getDownloadUrl() == null) { if (mProgressDialog != null && mProgressDialog.isShowing()) { mProgressDialog.dismiss(); } Toast.makeText(SearchResultActivity.this, R.string.no_download_link, Toast.LENGTH_SHORT).show(); return; } DownloadInfo download = new DownloadInfo(mp3.getDownloadUrl(), MusicInfo.downloadPath(mp3)); mDownloadService.insertDownload(download); Intent intent = new Intent(SearchResultActivity.this, DownloadActivity.class); startActivity(intent); if (mProgressDialog != null && mProgressDialog.isShowing()) { mProgressDialog.dismiss(); } } } private class FetchMp3LinkTaskForPreview extends AsyncTask<MusicInfo, Void, MusicInfo> { protected MusicInfo doInBackground(MusicInfo... mp3s) { MusicInfo mp3 = mp3s[0]; sFetcher.setMusicDownloadUrl(SearchResultActivity.this, mp3); return mp3; } protected void onPostExecute(MusicInfo mp3) { if (mp3.getDownloadUrl() == null) { if (mStreaming != null && mStreaming.isShowing()) { mStreaming.dismiss(); } if (mProgressDialog != null && mProgressDialog.isShowing()) { mProgressDialog.dismiss(); } Toast.makeText(SearchResultActivity.this, R.string.no_download_link, Toast.LENGTH_SHORT).show(); return; } if (mProgressDialog != null && mProgressDialog.isShowing()) { mProgressDialog.dismiss(); } playMusic(mp3); } } private void handleSearchResult(ArrayList<MusicInfo> mp3List) { if (mAdapter == null) { mAdapter = new Mp3ListAdapter( SearchResultActivity.this, R.layout.bill_item); setListAdapter(mAdapter); } // Uncomment until it is really working. //mp3List = Utils.dedupMp3List(mp3List); if (mp3List != null) { if (sData == null) sData = new Mp3ListWrapper(); if (mp3List.size() > 0) { sData.append(mp3List); } else { sHasMoreData = false; if (sData.size() == 0) { mProgressBar.setVisibility(View.GONE); if (!TextUtils.isEmpty(sQuery)) { mSearchMessage.setText("Sorry, we didn't find any result for \"" + sQuery + "\""); } else { mSearchMessage.setText(getString(R.string.no_result)); } } } mAdapter.setStatus(ListStatusView.Status.LOADED); mAdapter.notifyDataSetChanged(); } else { mAdapter.setStatus(ListStatusView.Status.ERROR); mAdapter.notifyDataSetChanged(); } } private static class FetchMp3ListTask extends AsyncTask<Void, Void, ArrayList<MusicInfo>> { Context mContext; public FetchMp3ListTask(Context context) { super(); mContext = context; } @Override protected void onPostExecute(ArrayList<MusicInfo> mp3List) { if (sFetchMp3ListTask != this) { // A new query is going on? return; } sFetchMp3ListTask = null; if (sSearchActivity != null) { sSearchActivity.handleSearchResult(mp3List); } } @Override protected ArrayList<MusicInfo> doInBackground(Void... params) { return sFetcher.getNextResultList(mContext); } } // A thread safe wrapper around ArrayList<MP3Info>. private final class Mp3ListWrapper { private ArrayList<MusicInfo> mMp3List; private ReadWriteLock lock = new ReentrantReadWriteLock(); private Lock r = lock.readLock(); private Lock w = lock.writeLock(); public Mp3ListWrapper() { mMp3List = new ArrayList<MusicInfo>(); } public void clear() { w.lock(); try { mMp3List.clear(); if (mAdapter != null) mAdapter.notifyDataSetInvalidated(); } finally { w.unlock(); } } public void add(MusicInfo info) { w.lock(); try { mMp3List.add(info); if (mAdapter != null) mAdapter.notifyDataSetChanged(); } finally { w.unlock(); } } public void append(ArrayList<MusicInfo> mp3List) { w.lock(); try { mMp3List.addAll(mp3List); mAdapter.notifyDataSetChanged(); } finally { w.unlock(); } } public int size() { r.lock(); try { return mMp3List.size(); } finally { r.unlock(); } } public MusicInfo get(int i) { r.lock(); try { return mMp3List.get(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 ListStatusView.Status mStatus; public Mp3ListAdapter(Context context, int resource) { mResource = resource; mInflater = (LayoutInflater)context.getSystemService( Context.LAYOUT_INFLATER_SERVICE); mStatus = ListStatusView.Status.LOADED; } public void setStatus(ListStatusView.Status status) { mStatus = status; } @Override public int getCount() { boolean showFooter = mStatus == ListStatusView.Status.ERROR || mStatus == ListStatusView.Status.LOADING; int footerCount = showFooter ? 1 : 0; if (sData == null) return footerCount; return sData.size() + footerCount; } @Override public Object getItem(int position) { if (sData == null) return null; if (position < sData.size()) return sData.get(position); return null; // footer. } @Override public long getItemId(int position) { if (sData == null) return -1; if (position < sData.size()) return position; return -1; // footer. } @Override public int getViewTypeCount() { return 2; } @Override public int getItemViewType(int position) { if (sData == null || position == sData.size()) { return VIEW_TYPE_FOOTER; } return VIEW_TYPE_NORMAL; } @Override public View getView(int position, View convertView, ViewGroup parent) { boolean isFooter = (sData == null || position == sData.size()); if (isFooter) { ListStatusView footerView = (ListStatusView) convertView; if (footerView == null) { footerView = (ListStatusView) mInflater.inflate( R.layout.liststatus, null); TextView text = (TextView)footerView.findViewById(R.id.prompt); text.setText(R.string.loading_more); } if (mStatus == ListStatusView.Status.LOADING) { footerView.setLoadingStatus(); } else if (mStatus == ListStatusView.Status.ERROR) { footerView.setErrorStatus(mRetryListener); } return footerView; } View v; Object item = sData.get(position); if (convertView == null) { v = mInflater.inflate(mResource, parent, false); } else { v = convertView; } MusicInfo info = (MusicInfo) item; ((TextView) v.findViewById(R.id.bill_title)).setText(info.getTitle()); ((TextView) v.findViewById(R.id.bill_artist)).setText(info.getArtist()); ((TextView)v.findViewById(R.id.bill_size)).setText(info.getDisplayFileSize()); if (sHasMoreData && position == sData.size() - 1 && mStatus == ListStatusView.Status.LOADED) { mHandler.post(new Runnable() { @Override public void run() { fetchNextMp3ListBatch(getApplication()); setStatus(ListStatusView.Status.LOADING); notifyDataSetChanged(); } }); } return v; } } public static void handleMp3ListIntent(Context context, String keyWords) { Intent intent = new Intent(context, SearchResultActivity.class); intent.putExtra(Constants.QUERY, keyWords); context.startActivity(intent); } }