/* * 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 java.util.List; import android.app.Activity; import android.app.Fragment; import android.app.LoaderManager.LoaderCallbacks; import android.content.AsyncTaskLoader; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.Loader; import android.os.Bundle; import android.os.SystemClock; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.widget.*; import android.widget.CompoundButton.OnCheckedChangeListener; import com.andrew.apollo.MusicPlaybackService; 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.core.FileDescriptor; import com.bt.download.android.gui.Peer; import com.bt.download.android.gui.PeerManager; import com.bt.download.android.gui.adapters.FileListAdapter; import com.bt.download.android.gui.util.UIUtils; import com.bt.download.android.gui.views.AbstractFragment; import com.bt.download.android.gui.views.BrowsePeerSearchBarView; import com.bt.download.android.gui.views.BrowsePeerSearchBarView.OnActionListener; import com.bt.download.android.gui.views.OverScrollListener; import com.frostwire.localpeer.Finger; import com.frostwire.logging.Logger; import com.frostwire.uxstats.UXAction; import com.frostwire.uxstats.UXStats; /** * @author gubatron * @author aldenml * */ public class BrowsePeerFragment extends AbstractFragment implements LoaderCallbacks<Object>, MainFragment { private static final Logger log = Logger.getLogger(BrowsePeerFragment.class); private static final int LOADER_FINGER_ID = 0; private static final int LOADER_FILES_ID = 1; private final BroadcastReceiver broadcastReceiver; private RadioButton buttonAudio; private RadioButton buttonRingtones; private RadioButton buttonVideos; private RadioButton buttonPictures; private RadioButton buttonApplications; private RadioButton buttonDocuments; private BrowsePeerSearchBarView filesBar; private com.bt.download.android.gui.views.ListView list; private FileListAdapter adapter; private Peer peer; private boolean local; private Finger finger; private View header; private OnRefreshSharedListener onRefreshSharedListener; private long lastAdapterRefresh; public BrowsePeerFragment() { super(R.layout.fragment_browse_peer); broadcastReceiver = new LocalBroadcastReceiver(); } public Peer getPeer() { if (peer == null) { loadPeerFromBundleData(); } if (peer == null) { loadPeerFromIntentData(); } return peer; } public void setPeer(Peer peer) { this.peer = peer; this.local = peer.isLocalHost(); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setRetainInstance(true); if (peer == null) { getPeer(); } if (peer == null) { // save move getActivity().finish(); return; } } @Override public Loader<Object> onCreateLoader(int id, Bundle args) { if (id == LOADER_FINGER_ID) { return createLoaderFinger(); } else if (id == LOADER_FILES_ID) { return createLoaderFiles(args.getByte("fileType")); } return null; } @Override public void onLoadFinished(Loader<Object> loader, Object data) { if (data == null) { log.warn("Something wrong, data is null"); removePeerAndFinish(); return; } if (loader.getId() == LOADER_FINGER_ID) { boolean firstCheck = finger == null; finger = (Finger) data; if (firstCheck) { checkNoEmptyButton(finger); } } else if (loader.getId() == LOADER_FILES_ID) { updateFiles((Object[]) data); } updateHeader(); } @Override public void onLoaderReset(Loader<Object> loader) { } @Override public void onResume() { super.onResume(); initBroadcastReceiver(); getLoaderManager().destroyLoader(LOADER_FINGER_ID); getLoaderManager().restartLoader(LOADER_FINGER_ID, null, this); if (adapter != null) { //adapter.notifyDataSetChanged(); browseFilesButtonClick(adapter.getFileType()); } else { //browseFilesButtonClick(Constants.FILE_TYPE_AUDIO); } } private void initBroadcastReceiver() { final IntentFilter filter = new IntentFilter(); filter.addAction(Constants.ACTION_REFRESH_FINGER); filter.addAction(Constants.ACTION_MEDIA_PLAYER_PLAY); filter.addAction(Constants.ACTION_MEDIA_PLAYER_PAUSED); filter.addAction(Constants.ACTION_MEDIA_PLAYER_STOPPED); filter.addAction(MusicPlaybackService.META_CHANGED); filter.addAction(MusicPlaybackService.PLAYSTATE_CHANGED); getActivity().registerReceiver(broadcastReceiver, filter); } @Override public void onPause() { super.onPause(); getActivity().unregisterReceiver(broadcastReceiver); } @Override public View getHeader(Activity activity) { LayoutInflater inflater = LayoutInflater.from(activity); header = inflater.inflate(R.layout.view_browse_peer_header, null); updateHeader(); return header; } public OnRefreshSharedListener getOnRefreshSharedListener() { return onRefreshSharedListener; } public void setOnRefreshSharedListener(OnRefreshSharedListener l) { this.onRefreshSharedListener = l; } @Override protected void initComponents(View v) { buttonApplications = initRadioButton(v, R.id.fragment_browse_peer_radio_applications, Constants.FILE_TYPE_APPLICATIONS); buttonDocuments = initRadioButton(v, R.id.fragment_browse_peer_radio_documents, Constants.FILE_TYPE_DOCUMENTS); buttonPictures = initRadioButton(v, R.id.fragment_browse_peer_radio_pictures, Constants.FILE_TYPE_PICTURES); buttonVideos = initRadioButton(v, R.id.fragment_browse_peer_radio_videos, Constants.FILE_TYPE_VIDEOS); buttonRingtones = initRadioButton(v, R.id.fragment_browse_peer_radio_ringtones, Constants.FILE_TYPE_RINGTONES); buttonAudio = initRadioButton(v, R.id.fragment_browse_peer_radio_audio, Constants.FILE_TYPE_AUDIO); filesBar = findView(v, R.id.fragment_browse_peer_files_bar); filesBar.setOnActionListener(new OnActionListener() { public void onCheckAll(View v, boolean isChecked) { if (adapter != null) { if (isChecked) { adapter.checkAll(); } else { adapter.clearChecked(); } } } public void onFilter(View v, String str) { if (adapter != null) { adapter.getFilter().filter(str); } } }); list = findView(v, R.id.fragment_browse_peer_list); list.setOverScrollListener(new OverScrollListener() { @Override public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { long now = SystemClock.elapsedRealtime(); if (clampedY && (now - lastAdapterRefresh) > 5000) { refreshSelection(); } } }); } protected void onRefreshShared(byte fileType) { try { if (onRefreshSharedListener != null && finger != null) { int numShared = 0; switch (fileType) { case Constants.FILE_TYPE_APPLICATIONS: numShared = finger.numSharedApplicationFiles; UXStats.instance().log(UXAction.LIBRARY_BROWSE_FILE_TYPE_APPLICATIONS); break; case Constants.FILE_TYPE_AUDIO: numShared = finger.numSharedAudioFiles; UXStats.instance().log(UXAction.LIBRARY_BROWSE_FILE_TYPE_AUDIO); break; case Constants.FILE_TYPE_DOCUMENTS: numShared = finger.numSharedDocumentFiles; UXStats.instance().log(UXAction.LIBRARY_BROWSE_FILE_TYPE_DOCUMENTS); break; case Constants.FILE_TYPE_PICTURES: numShared = finger.numSharedPictureFiles; UXStats.instance().log(UXAction.LIBRARY_BROWSE_FILE_TYPE_PICTURES); break; case Constants.FILE_TYPE_RINGTONES: numShared = finger.numSharedRingtoneFiles; UXStats.instance().log(UXAction.LIBRARY_BROWSE_FILE_TYPE_RINGTONES); break; case Constants.FILE_TYPE_VIDEOS: numShared = finger.numSharedVideoFiles; UXStats.instance().log(UXAction.LIBRARY_BROWSE_FILE_TYPE_VIDEOS); break; } onRefreshSharedListener.onRefresh(this, fileType, numShared); } } catch (Throwable e) { // this catch is mostly due to the mutable nature of finger and onRefreshSharedListener log.error("Error notifying shared refresh", e); } } private void loadPeerFromIntentData() { if (peer != null) { // why? return; } Intent intent = getActivity().getIntent(); if (intent.hasExtra(Constants.EXTRA_PEER_UUID)) { String uuid = intent.getStringExtra(Constants.EXTRA_PEER_UUID); if (uuid != null) { try { peer = PeerManager.instance().findPeerByKey(uuid); local = peer.isLocalHost(); } catch (Throwable e) { peer = null; // weird situation reported by a strange bug. } } } } private void loadPeerFromBundleData() { if (peer != null) { // why? return; } Bundle bundle = getArguments(); if (bundle != null && bundle.containsKey(Constants.EXTRA_PEER_UUID)) { String uuid = bundle.getString(Constants.EXTRA_PEER_UUID); if (uuid != null) { try { peer = PeerManager.instance().findPeerByKey(uuid); local = peer.isLocalHost(); } catch (Throwable e) { peer = null; // weird situation reported by a strange bug. } } } } private RadioButton initRadioButton(View v, int viewId, final byte fileType) { final RadioButton button = findView(v, viewId); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (button.isChecked()) { browseFilesButtonClick(fileType); } } }); button.setOnCheckedChangeListener(new OnCheckedChangeListener() { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { browseFilesButtonClick(fileType); } } }); return button; } private void browseFilesButtonClick(byte fileType) { if (adapter != null) { saveListViewVisiblePosition(adapter.getFileType()); adapter.clear(); } filesBar.clearCheckAll(); filesBar.clearSearch(); getLoaderManager().destroyLoader(LOADER_FILES_ID); Bundle bundle = new Bundle(); bundle.putByte("fileType", fileType); getLoaderManager().restartLoader(LOADER_FILES_ID, bundle, this); onRefreshShared(fileType); } private Loader<Object> createLoaderFinger() { AsyncTaskLoader<Object> loader = new AsyncTaskLoader<Object>(getActivity()) { @Override public Object loadInBackground() { try { return peer.finger(); } catch (Throwable e) { log.error("Error performing finger", e); } return null; } }; loader.forceLoad(); return loader; } private Loader<Object> createLoaderFiles(final byte fileType) { AsyncTaskLoader<Object> loader = new AsyncTaskLoader<Object>(getActivity()) { @Override public Object loadInBackground() { try { return new Object[] { fileType, peer.browse(fileType) }; } catch (Throwable e) { log.error("Error performing finger", e); } return null; } }; loader.forceLoad(); return loader; } private void updateHeader() { if (finger == null) { if (peer == null) { log.warn("Something wrong, finger and peer are null"); removePeerAndFinish(); return; } else { finger = peer.finger(); } } if (header != null) { byte fileType = adapter != null ? adapter.getFileType() : Constants.FILE_TYPE_AUDIO; int numShared = 0; int numTotal = 0; switch (fileType) { case Constants.FILE_TYPE_APPLICATIONS: numShared = finger.numSharedApplicationFiles; numTotal = finger.numTotalApplicationFiles; break; case Constants.FILE_TYPE_AUDIO: numShared = finger.numSharedAudioFiles; numTotal = finger.numTotalAudioFiles; break; case Constants.FILE_TYPE_DOCUMENTS: numShared = finger.numSharedDocumentFiles; numTotal = finger.numTotalDocumentFiles; break; case Constants.FILE_TYPE_PICTURES: numShared = finger.numSharedPictureFiles; numTotal = finger.numTotalPictureFiles; break; case Constants.FILE_TYPE_RINGTONES: numShared = finger.numSharedRingtoneFiles; numTotal = finger.numTotalRingtoneFiles; break; case Constants.FILE_TYPE_VIDEOS: numShared = finger.numSharedVideoFiles; numTotal = finger.numTotalVideoFiles; break; } String fileTypeStr = getString(R.string.my_filetype, UIUtils.getFileTypeAsString(getResources(), fileType)); TextView title = (TextView) header.findViewById(R.id.view_browse_peer_header_text_title); TextView total = (TextView) header.findViewById(R.id.view_browse_peer_header_text_total); TextView shared = (TextView) header.findViewById(R.id.view_browse_peer_header_text_total_shared); TextView unshared = (TextView) header.findViewById(R.id.view_browse_peer_header_text_total_unshared); FileVisibilityFilterListener visibilityFilterListener = new FileVisibilityFilterListener(); shared.setOnClickListener(visibilityFilterListener); unshared.setOnClickListener(visibilityFilterListener); title.setText(fileTypeStr); total.setText("(" + String.valueOf(numTotal) + ")"); shared.setText(String.valueOf(numShared)); unshared.setText(String.valueOf(numTotal - numShared)); updateFileVisiblityIndicatorsAlpha(); if (fileType == Constants.FILE_TYPE_AUDIO) { buttonAudio.setChecked(true); } } if (adapter != null) { onRefreshShared(adapter.getFileType()); } else { browseFilesButtonClick(Constants.FILE_TYPE_AUDIO); } restoreListViewScrollPosition(); } private void restoreListViewScrollPosition() { if (adapter != null) { int savedListViewVisiblePosition = getSavedListViewVisiblePosition(adapter.getFileType()); savedListViewVisiblePosition = (savedListViewVisiblePosition > 0) ? savedListViewVisiblePosition + 1 : 0; list.setSelection(savedListViewVisiblePosition); } } private void updateFiles(Object[] data) { if (data == null) { log.warn("Something wrong, data is null"); removePeerAndFinish(); return; } try { byte fileType = (Byte) data[0]; @SuppressWarnings("unchecked") List<FileDescriptor> items = (List<FileDescriptor>) data[1]; adapter = new FileListAdapter(list.getContext(), items, peer, local, fileType) { @Override protected void onItemChecked(View v, boolean isChecked) { if (!isChecked) { filesBar.clearCheckAll(); } } @Override protected void onLocalPlay() { if (adapter != null) { saveListViewVisiblePosition(adapter.getFileType()); } } }; adapter.setCheckboxesVisibility(true); list.setAdapter(adapter); } catch (Throwable e) { log.error("Error updating files in list", e); } } private void checkNoEmptyButton(Finger f) { if (f.numSharedAudioFiles > 0) { buttonAudio.setChecked(true); } else if (f.numSharedVideoFiles > 0) { buttonVideos.setChecked(true); } else if (f.numSharedPictureFiles > 0) { buttonPictures.setChecked(true); } else if (f.numSharedDocumentFiles > 0) { buttonDocuments.setChecked(true); } else if (f.numSharedApplicationFiles > 0) { buttonApplications.setChecked(true); } else if (f.numSharedRingtoneFiles > 0) { buttonRingtones.setChecked(true); } else { buttonAudio.setChecked(true); } } private void removePeerAndFinish() { Activity activity = getActivity(); if (activity != null) { if (peer != null) { try { UIUtils.showShortMessage(activity, R.string.is_not_responding, peer.getNickname()); PeerManager.instance().removePeer(peer); } catch (Throwable e) { // still possible to get an exception since peer is mutable. log.error("Error removing a not null peer", e); } } activity.finish(); } } private void saveListViewVisiblePosition(byte fileType) { int firstVisiblePosition = list.getFirstVisiblePosition(); ConfigurationManager.instance().setInt(Constants.BROWSE_PEER_FRAGMENT_LISTVIEW_FIRST_VISIBLE_POSITION + fileType, firstVisiblePosition); } private int getSavedListViewVisiblePosition(byte fileType) { //will return 0 if not found. return ConfigurationManager.instance().getInt(Constants.BROWSE_PEER_FRAGMENT_LISTVIEW_FIRST_VISIBLE_POSITION + fileType); } private final class LocalBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(Constants.ACTION_MEDIA_PLAYER_PLAY) || action.equals(Constants.ACTION_MEDIA_PLAYER_STOPPED) || action.equals(Constants.ACTION_MEDIA_PLAYER_PAUSED) || action.equals(Constants.ACTION_MEDIA_PLAYER_PAUSED) || action.equals(MusicPlaybackService.PLAYSTATE_CHANGED) || action.equals(MusicPlaybackService.META_CHANGED) ) { if (adapter != null) { adapter.notifyDataSetChanged(); } } else if (intent.getAction().equals(Constants.ACTION_REFRESH_FINGER)) { try { getLoaderManager().restartLoader(LOADER_FINGER_ID, null, BrowsePeerFragment.this); } catch (Throwable t) { log.error("LocalBroadcastReceiver can't restart loader on ACTION_REFRESH_FINGER, fragment not attached?", t); } } } } private class FileVisibilityFilterListener implements OnClickListener { @Override public void onClick(View v) { log.debug("clicked filter"); if (adapter != null) { adapter.setFileVisibilityBySharedState((adapter.getFileVisibilityBySharedState() + 1) % 3); adapter.getFilter().filter(filesBar.getText()); } updateFileVisiblityIndicatorsAlpha(); } } public void updateFileVisiblityIndicatorsAlpha() { if (adapter == null) { return; } TextView shared = (TextView) header.findViewById(R.id.view_browse_peer_header_text_total_shared); TextView unshared = (TextView) header.findViewById(R.id.view_browse_peer_header_text_total_unshared); int transparentValue = 128; switch (adapter.getFileVisibilityBySharedState()) { case FileListAdapter.FILE_LIST_FILTER_SHOW_ALL: UIUtils.setTextViewAlpha(shared, 255); UIUtils.setTextViewAlpha(unshared, 255); break; case FileListAdapter.FILE_LIST_FILTER_SHOW_SHARED: UIUtils.setTextViewAlpha(shared, 255); UIUtils.setTextViewAlpha(unshared, transparentValue); break; case FileListAdapter.FILE_LIST_FILTER_SHOW_UNSHARED: UIUtils.setTextViewAlpha(shared, transparentValue); UIUtils.setTextViewAlpha(unshared, 255); break; } } public static interface OnRefreshSharedListener { public void onRefresh(Fragment f, byte fileType, int numShared); } public void refreshSelection() { if (adapter != null) { lastAdapterRefresh = SystemClock.elapsedRealtime(); browseFilesButtonClick(adapter.getFileType()); } } }