/*
* Created by Angel Leon (@gubatron), Alden Torres (aldenml)
* Copyright (c) 2011-2014, FrostWire(R). All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.bt.download.android.gui.fragments;
import com.bt.download.android.R;
import com.bt.download.android.core.ConfigurationManager;
import com.bt.download.android.core.Constants;
import com.bt.download.android.gui.LocalSearchEngine;
import com.bt.download.android.gui.activities.MainActivity;
import com.bt.download.android.gui.adapters.SearchResultListAdapter;
import com.bt.download.android.gui.adapters.SearchResultListAdapter.FilteredSearchResults;
import com.bt.download.android.gui.dialogs.NewTransferDialog;
import com.bt.download.android.gui.tasks.DownloadSoundcloudFromUrlTask;
import com.bt.download.android.gui.tasks.StartDownloadTask;
import com.bt.download.android.gui.transfers.HttpSlideSearchResult;
import com.bt.download.android.gui.transfers.TransferManager;
import com.bt.download.android.gui.util.UIUtils;
import com.bt.download.android.gui.views.AbstractDialog;
import com.bt.download.android.gui.views.AbstractDialog.OnDialogClickListener;
import com.bt.download.android.gui.views.AbstractFragment;
import com.bt.download.android.gui.views.PromotionsView;
import com.bt.download.android.gui.views.PromotionsView.OnPromotionClickListener;
import com.bt.download.android.gui.views.SearchInputView;
import com.bt.download.android.gui.views.SearchInputView.OnSearchListener;
import com.bt.download.android.gui.views.SearchProgressView;
import com.frostwire.frostclick.Slide;
import com.frostwire.frostclick.SlideList;
import com.frostwire.frostclick.TorrentPromotionSearchResult;
import com.frostwire.logging.Logger;
import com.frostwire.search.FileSearchResult;
import com.frostwire.search.HttpSearchResult;
import com.frostwire.search.SearchManagerListener;
import com.frostwire.search.SearchPerformer;
import com.frostwire.search.SearchResult;
import com.frostwire.search.torrent.TorrentCrawledSearchResult;
import com.frostwire.search.torrent.TorrentSearchResult;
import com.frostwire.util.HttpClient;
import com.frostwire.util.HttpClientFactory;
import com.frostwire.util.JsonUtils;
import com.frostwire.util.Ref;
import com.frostwire.uxstats.UXAction;
import com.frostwire.uxstats.UXStats;
import com.umeng.analytics.MobclickAgent;
import com.wandoujia.ads.sdk.Ads;
import com.wandoujia.ads.sdk.loader.Fetcher;
import com.wandoujia.ads.sdk.widget.AdBanner;
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.FrameLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author gubatron
* @author aldenml
*
*/
public final class SearchFragment extends AbstractFragment implements MainFragment, OnDialogClickListener {
private static final Logger LOG = Logger.getLogger(SearchFragment.class);
private SearchResultListAdapter adapter;
private List<Slide> slides;
private SearchInputView searchInput;
private ProgressBar deepSearchProgress;
private PromotionsView promotions;
private SearchProgressView searchProgress;
private ListView list;
private final FileTypeCounter fileTypeCounter;
public AdBanner adBanner;
private View adBannerView;
public SearchFragment() {
super(R.layout.fragment_search);
fileTypeCounter = new FileTypeCounter();
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setupAdapter();
if (slides != null) {
promotions.setSlides(slides);
} else {
new LoadSlidesTask(this).execute();
}
setRetainInstance(true);
}
@Override
public View getHeader(Activity activity) {
LayoutInflater inflater = LayoutInflater.from(activity);
TextView header = (TextView) inflater.inflate(R.layout.view_main_fragment_simple_header, null);
header.setText(R.string.search);
return header;
}
@Override
public void onResume() {
super.onResume();
if (adapter != null && (adapter.getCount() > 0 || adapter.getTotalCount() > 0)) {
refreshFileTypeCounters(true);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
//No call for super(). Bug on API Level > 11.
}
@Override
protected void initComponents(final View view) {
searchInput = findView(view, R.id.fragment_search_input);
searchInput.setShowKeyboardOnPaste(true);
searchInput.setOnSearchListener(new OnSearchListener() {
@Override
public void onSearch(View v, String query, int mediaTypeId) {
MobclickAgent.onEvent(getActivity(), "search_keyword", query);
if (query.contains("://m.soundcloud.com/") || query.contains("://soundcloud.com/")) {
cancelSearch(view);
new DownloadSoundcloudFromUrlTask(getActivity(), query).execute();
searchInput.setText("");
} else if (query.contains("youtube.com/")) {
performYTSearch(query);
} else if (query.startsWith("magnet:?xt=urn:btih:")) {
startMagnetDownload(query);
searchInput.setText("");
}
else {
performSearch(query, mediaTypeId);
}
}
@Override
public void onMediaTypeSelected(View v, int mediaTypeId) {
adapter.setFileType(mediaTypeId);
showSearchView(view);
}
@Override
public void onClear(View v) {
cancelSearch(view);
}
});
deepSearchProgress = findView(view, R.id.fragment_search_deepsearch_progress);
deepSearchProgress.setVisibility(View.GONE);
promotions = findView(view, R.id.fragment_search_promos);
promotions.setOnPromotionClickListener(new OnPromotionClickListener() {
@Override
public void onPromotionClick(PromotionsView v, Slide slide) {
startPromotionDownload(slide);
}
});
searchProgress = findView(view, R.id.fragment_search_search_progress);
searchProgress.setCancelOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (LocalSearchEngine.instance().isSearchFinished()) {
performSearch(searchInput.getText(), adapter.getFileType()); // retry
} else {
cancelSearch(view);
}
}
});
list = findView(view, R.id.fragment_search_list);
showSearchView(view);
showBannerAd(view);
}
/**
* 显示banner广告
* */
public void showBannerAd(View view) {
FrameLayout containerView = (FrameLayout)findView(view, R.id.fragment_search_banner_ad_container);
if (adBannerView != null && containerView.indexOfChild(adBannerView) >= 0) {
containerView.removeView(adBannerView);
}
if (Ads.isLoaded(Fetcher.AdFormat.banner, Constants.TAG_BANNER)) {
containerView.setVisibility(View.VISIBLE);
adBanner = Ads.showBannerAd(getActivity(), containerView,
Constants.TAG_BANNER);
adBannerView = adBanner.getView();
//如果当前选中的不是传输界面
Fragment Fragment = ((MainActivity) getActivity()).getCurrentFragment();
if (Fragment != null && !(Fragment instanceof TransfersFragment)) {
adBanner.stopAutoScroll();
}
} else {
containerView.setVisibility(View.GONE);
}
}
private void startMagnetDownload(String magnet) {
//Show me the transfer tab
Intent i = new Intent(getActivity(), MainActivity.class);
i.setAction(Constants.ACTION_SHOW_TRANSFERS);
i.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(i);
//go!
TransferManager.instance().downloadTorrent(magnet);
}
private static String extractYTId(String ytUrl) {
String vId = null;
Pattern pattern = Pattern.compile(".*(?:youtu.be\\/|v\\/|u\\/\\w\\/|embed\\/|watch\\?v=)([^#\\&\\?]*).*");
Matcher matcher = pattern.matcher(ytUrl);
if (matcher.matches()){
vId = matcher.group(1);
}
return vId;
}
private void setupAdapter() {
if (adapter == null) {
adapter = new SearchResultListAdapter(getActivity()) {
@Override
protected void searchResultClicked(SearchResult sr) {
startTransfer(sr, getString(R.string.download_added_to_queue));
}
};
LocalSearchEngine.instance().registerListener(new SearchManagerListener() {
@Override
public void onResults(SearchPerformer performer, final List<? extends SearchResult> results) {
@SuppressWarnings("unchecked")
FilteredSearchResults fsr = adapter.filter((List<SearchResult>) results);
final List<SearchResult> filteredList = fsr.filtered;
fileTypeCounter.add(fsr);
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
adapter.addResults(results, filteredList);
showSearchView(getView());
refreshFileTypeCounters(true);
}
});
}
@Override
public void onFinished(long token) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
searchProgress.setProgressEnabled(false);
deepSearchProgress.setVisibility(View.GONE);
}
});
}
});
}
list.setAdapter(adapter);
}
private void refreshFileTypeCounters(boolean fileTypeCountersVisible) {
searchInput.updateFileTypeCounter(Constants.FILE_TYPE_APPLICATIONS, fileTypeCounter.fsr.numApplications);
searchInput.updateFileTypeCounter(Constants.FILE_TYPE_AUDIO, fileTypeCounter.fsr.numAudio);
searchInput.updateFileTypeCounter(Constants.FILE_TYPE_DOCUMENTS, fileTypeCounter.fsr.numDocuments);
searchInput.updateFileTypeCounter(Constants.FILE_TYPE_PICTURES, fileTypeCounter.fsr.numPictures);
searchInput.updateFileTypeCounter(Constants.FILE_TYPE_TORRENTS, fileTypeCounter.fsr.numTorrents);
searchInput.updateFileTypeCounter(Constants.FILE_TYPE_VIDEOS, fileTypeCounter.fsr.numVideo);
searchInput.setFileTypeCountersVisible(fileTypeCountersVisible);
}
public void performYTSearch(String query) {
String ytId=extractYTId(query);
if (ytId != null){
searchInput.setText("");
searchInput.performClickOnRadioButton(Constants.FILE_TYPE_VIDEOS);
performSearch(ytId,Constants.FILE_TYPE_VIDEOS);
searchInput.setHint(getActivity().getString(R.string.searching_for) + " youtube:"+ytId);
}
}
private void performSearch(String query, int mediaTypeId) {
adapter.clear();
adapter.setFileType(mediaTypeId);
fileTypeCounter.clear();
refreshFileTypeCounters(false);
LocalSearchEngine.instance().performSearch(query);
searchProgress.setProgressEnabled(true);
showSearchView(getView());
UXStats.instance().log(UXAction.SEARCH_STARTED_ENTER_KEY);
}
private void cancelSearch(View view) {
adapter.clear();
fileTypeCounter.clear();
refreshFileTypeCounters(false);
LocalSearchEngine.instance().cancelSearch();
searchProgress.setProgressEnabled(false);
showSearchView(getView());
}
private void showSearchView(View view) {
if (LocalSearchEngine.instance().isSearchStopped()) {
switchView(view, R.id.fragment_search_promos);
deepSearchProgress.setVisibility(View.GONE);
} else {
if (adapter != null && adapter.getCount() > 0) {
switchView(view, R.id.fragment_search_list);
deepSearchProgress.setVisibility(LocalSearchEngine.instance().isSearchFinished() ? View.GONE : View.VISIBLE);
} else {
switchView(view, R.id.fragment_search_search_progress);
deepSearchProgress.setVisibility(View.GONE);
}
}
searchProgress.setProgressEnabled(!LocalSearchEngine.instance().isSearchFinished());
}
private void switchView(View v, int id) {
if (v != null) {
FrameLayout frameLayout = findView(v, R.id.fragment_search_framelayout);
int childCount = frameLayout.getChildCount();
for (int i = 0; i < childCount; i++) {
View childAt = frameLayout.getChildAt(i);
childAt.setVisibility((childAt.getId() == id) ? View.VISIBLE : View.INVISIBLE);
}
}
}
@Override
public void onDialogClick(String tag, int which) {
if (tag.equals(NewTransferDialog.TAG) && which == AbstractDialog.BUTTON_POSITIVE) {
if (Ref.alive(NewTransferDialog.srRef)) {
startDownload(this.getActivity(), NewTransferDialog.srRef.get(), getString(R.string.download_added_to_queue));
}
}
}
private void startTransfer(final SearchResult sr, final String toastMessage) {
if (ConfigurationManager.instance().getBoolean(Constants.PREF_KEY_GUI_SHOW_NEW_TRANSFER_DIALOG)) {
if (sr instanceof FileSearchResult) {
NewTransferDialog dlg = NewTransferDialog.newInstance((FileSearchResult) sr, false);
dlg.show(getFragmentManager());
}
} else {
if (isVisible()) {
startDownload(getActivity(), sr, toastMessage);
}
}
uxLogAction(sr);
}
private static void startDownload(Context ctx, SearchResult sr, String message) {
StartDownloadTask task = new StartDownloadTask(ctx, sr, message);
UIUtils.showTransfersOnDownloadStart(ctx);
task.execute();
}
private void startPromotionDownload(Slide slide) {
SearchResult sr = null;
switch (slide.method) {
case Slide.DOWNLOAD_METHOD_TORRENT:
sr = new TorrentPromotionSearchResult(slide);
break;
case Slide.DOWNLOAD_METHOD_HTTP:
sr = new HttpSlideSearchResult(slide);
break;
default:
sr = null;
break;
}
if (sr == null) {
//check if there is a URL available to open a web browser.
if (slide.url != null) {
Intent i = new Intent("android.intent.action.VIEW", Uri.parse(slide.url));
getActivity().startActivity(i);
}
return;
}
startTransfer(sr, getString(R.string.downloading_promotion, sr.getDisplayName()));
}
private void uxLogAction(SearchResult sr) {
UXStats.instance().log(UXAction.SEARCH_RESULT_CLICKED);
if (sr instanceof HttpSearchResult) {
UXStats.instance().log(UXAction.DOWNLOAD_CLOUD_FILE);
} else if (sr instanceof TorrentSearchResult) {
if (sr instanceof TorrentCrawledSearchResult) {
UXStats.instance().log(UXAction.DOWNLOAD_PARTIAL_TORRENT_FILE);
} else {
UXStats.instance().log(UXAction.DOWNLOAD_FULL_TORRENT_FILE);
}
}
}
private static class LoadSlidesTask extends AsyncTask<Void, Void, List<Slide>> {
private final WeakReference<SearchFragment> fragment;
public LoadSlidesTask(SearchFragment fragment) {
this.fragment = new WeakReference<SearchFragment>(fragment);
}
@Override
protected List<Slide> doInBackground(Void... params) {
try {
HttpClient http = HttpClientFactory.newInstance();
String url = String.format("%s?from=android&fw=%s&sdk=%s", Constants.SERVER_PROMOTIONS_URL, Constants.FROSTWIRE_VERSION_STRING, Build.VERSION.SDK_INT);
String json = http.get(url);
SlideList slides = JsonUtils.toObject(json, SlideList.class);
return slides.slides;
} catch (Throwable e) {
LOG.error("Error loading slides from url", e);
}
return null;
}
@Override
protected void onPostExecute(List<Slide> result) {
SearchFragment f = fragment.get();
if (f != null && result != null && !result.isEmpty()) {
f.slides = result;
f.promotions.setSlides(result);
}
}
}
private static final class FileTypeCounter {
private final FilteredSearchResults fsr = new FilteredSearchResults();
public void add(FilteredSearchResults fsr) {
this.fsr.numAudio += fsr.numAudio;
this.fsr.numApplications += fsr.numApplications;
this.fsr.numDocuments += fsr.numDocuments;
this.fsr.numPictures += fsr.numPictures;
this.fsr.numTorrents += fsr.numTorrents;
this.fsr.numVideo += fsr.numVideo;
}
public void clear() {
this.fsr.numAudio = 0;
this.fsr.numApplications = 0;
this.fsr.numDocuments = 0;
this.fsr.numPictures = 0;
this.fsr.numTorrents = 0;
this.fsr.numVideo = 0;
}
}
}