com.frostwire.android.gui.fragments.MyFilesFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.frostwire.android.gui.fragments.MyFilesFragment.java

Source

/*
 * Created by Angel Leon (@gubatron), Alden Torres (aldenml)
 *            Marcelina Knitter (@marcelinkaaa)
 * Copyright (c) 2011-2018, FrostWire(R). All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.frostwire.android.gui.fragments;

import android.app.Activity;
import android.app.LoaderManager;
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.support.design.widget.TabLayout;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.view.ActionMode;
import android.support.v7.widget.SearchView;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.GridView;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.andrew.apollo.MusicPlaybackService;
import com.andrew.apollo.utils.MusicUtils;
import com.frostwire.android.AndroidPlatform;
import com.frostwire.android.R;
import com.frostwire.android.core.ConfigurationManager;
import com.frostwire.android.core.Constants;
import com.frostwire.android.core.FileDescriptor;
import com.frostwire.android.gui.Librarian;
import com.frostwire.android.gui.adapters.menu.AddToPlaylistMenuAction;
import com.frostwire.android.gui.adapters.menu.CopyMagnetMenuAction;
import com.frostwire.android.gui.adapters.menu.DeleteAdapterFilesMenuAction;
import com.frostwire.android.gui.adapters.menu.FileInformationAction;
import com.frostwire.android.gui.adapters.FileListAdapter;
import com.frostwire.android.gui.adapters.menu.OpenMenuAction;
import com.frostwire.android.gui.adapters.menu.RenameFileMenuAction;
import com.frostwire.android.gui.adapters.menu.SeedAction;
import com.frostwire.android.gui.adapters.menu.SendFileMenuAction;
import com.frostwire.android.gui.adapters.menu.SetAsRingtoneMenuAction;
import com.frostwire.android.gui.adapters.menu.SetAsWallpaperMenuAction;
import com.frostwire.android.gui.util.ScrollListeners;
import com.frostwire.android.gui.util.UIUtils;
import com.frostwire.android.gui.views.AbstractFragment;
import com.frostwire.android.gui.views.SwipeLayout;
import com.frostwire.util.Logger;
import com.frostwire.util.Ref;
import com.frostwire.uxstats.UXAction;
import com.frostwire.uxstats.UXStats;

import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static com.frostwire.android.util.Asyncs.async;

/**
 * @author aldenml
 * @author gubatron
 * @author marcelinkaaa
 */
public class MyFilesFragment extends AbstractFragment implements LoaderCallbacks<Object>, MainFragment {

    private static final Logger LOG = Logger.getLogger(MyFilesFragment.class);
    private static final int LOADER_FILES_ID = 0;
    private static final String EXTRA_LAST_FILE_TYPE_CLICKED = "com.frostwire.android.extra.byte.EXTRA_LAST_FILE_TYPE_CLICKED";

    private final BroadcastReceiver broadcastReceiver;

    private SwipeRefreshLayout swipeRefresh;
    private GridView list;
    private FileListAdapter adapter;
    private MenuItem checkBoxMenuItem;
    private RelativeLayout selectAllCheckboxContainer;
    private CheckBox selectAllCheckbox;
    private TabLayout tabLayout;
    private CompoundButton.OnCheckedChangeListener selectAllCheckboxListener;
    private boolean selectAllModeOn;
    private View header;
    private TextView headerTitle;
    private TextView headerTotal;
    private long lastAdapterRefresh;
    private String previousFilter;
    private final SparseArray<Set<FileListAdapter.FileDescriptorItem>> checkedItemsMap;

    // given the byte:fileType as the index, this array will match the corresponding UXAction code.
    // no if's necessary, random access -> O(1)
    private final int[] browseUXActions = { UXAction.LIBRARY_BROWSE_FILE_TYPE_AUDIO,
            UXAction.LIBRARY_BROWSE_FILE_TYPE_PICTURES, UXAction.LIBRARY_BROWSE_FILE_TYPE_VIDEOS,
            UXAction.LIBRARY_BROWSE_FILE_TYPE_DOCUMENTS, UXAction.LIBRARY_BROWSE_FILE_TYPE_APPLICATIONS,
            UXAction.LIBRARY_BROWSE_FILE_TYPE_RINGTONES, UXAction.LIBRARY_BROWSE_FILE_TYPE_TORRENTS };

    private final byte[] tabPositionToFileType = new byte[] { Constants.FILE_TYPE_AUDIO,
            Constants.FILE_TYPE_RINGTONES, Constants.FILE_TYPE_VIDEOS, Constants.FILE_TYPE_PICTURES,
            Constants.FILE_TYPE_DOCUMENTS, Constants.FILE_TYPE_TORRENTS };

    private final int[] fileTypeToTabPosition = new int[] { 0, // 0x0 Audio @ 0
            3, // 0x1 Picture @ 3
            2, // 0x2 Video @ 2
            4, // 0x3 Documents @ 4
            -1, // 0x4 Application @ N/A on My Files
            1, // 0x5 Ringtones @ 1
            5 // 0x6 Torrents @ 5
    };

    /**
     * This implements the toolbar's action mode view and its menu
     */
    private final MyFilesActionModeCallback selectionModeCallback;
    private byte lastFileType = Constants.FILE_TYPE_AUDIO;

    public MyFilesFragment() {
        super(R.layout.fragment_my_files);
        broadcastReceiver = new LocalBroadcastReceiver();
        setHasOptionsMenu(true);
        checkedItemsMap = new SparseArray<>();
        selectionModeCallback = new MyFilesActionModeCallback();
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (savedInstanceState != null) {
            lastFileType = savedInstanceState.getByte(EXTRA_LAST_FILE_TYPE_CLICKED);
            clickFileTypeTab(lastFileType);
        }
        setRetainInstance(true);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        outState.putByte(EXTRA_LAST_FILE_TYPE_CLICKED, lastFileType);
        super.onSaveInstanceState(outState);
    }

    @Override
    public Loader<Object> onCreateLoader(int id, Bundle args) {
        if (id == LOADER_FILES_ID && args != null) {
            return createLoaderFiles(args.getByte("fileType"));
        }
        return null;
    }

    @Override
    public void onLoadFinished(Loader<Object> loader, Object dataObj) {
        Object[] data = (Object[]) dataObj;
        if (data == null || data.length < 2 || data[1] == null) {
            LOG.warn("Something wrong, data is null");
            return;
        }

        byte fileType = (Byte) data[0];
        if (fileType != lastFileType) {
            // user already pressed another tab
            return;
        }

        if (loader.getId() == LOADER_FILES_ID) {
            updateFiles(data);
        }
        updateHeader();
        if (swipeRefresh != null) {
            swipeRefresh.setRefreshing(false);
        }
    }

    @Override
    public void onLoaderReset(Loader<Object> loader) {
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.fragment_my_files_menu, menu);
        super.onCreateOptionsMenu(menu, inflater);
        initToolbarSearchFilter(menu);
        initToolbarCheckbox(menu);
    }

    private void initToolbarCheckbox(Menu menu) {
        checkBoxMenuItem = menu.findItem(R.id.fragment_my_files_menu_checkbox);
        checkBoxMenuItem.setOnMenuItemClickListener(item -> {
            onToolbarMenuSelectModeCheckboxClick();
            return true;
        });
        refreshCheckBoxMenuItemVisibility();
        selectAllCheckbox.setOnCheckedChangeListener(selectAllCheckboxListener);
    }

    private void initToolbarSearchFilter(Menu menu) {
        final SearchView searchView = (SearchView) menu.findItem(R.id.fragment_my_files_menu_filter)
                .getActionView();
        searchView.setFocusable(true);
        if (isAdded()) {
            searchView.setQueryHint(getResources().getString(R.string.filter_ellipsis));
        }
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                performFilter(query);
                UIUtils.hideKeyboardFromActivity(getActivity());
                return true;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                performFilter(newText);
                return false;
            }
        });
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle item selection
        switch (item.getItemId()) {
        case R.id.fragment_my_files_menu_filter:
            return true;
        case R.id.fragment_my_files_menu_checkbox:
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        initBroadcastReceiver();
        if (adapter != null) {
            restorePreviouslyChecked();
            clickFileTypeTab(adapter.getFileType());
        }
        updateHeader();
    }

    private void restorePreviouslyChecked() {
        if (adapter != null) {
            Set<FileListAdapter.FileDescriptorItem> previouslyChecked = checkedItemsMap.get(adapter.getFileType());
            if (previouslyChecked != null && !previouslyChecked.isEmpty()) {
                adapter.setChecked(previouslyChecked);
            }
        }
    }

    private void savePreviouslyCheckedFileDescriptors() {
        if (adapter != null) {
            final Set<FileListAdapter.FileDescriptorItem> checked = adapter.getChecked();
            if (checked != null && !checked.isEmpty()) {
                Set<FileListAdapter.FileDescriptorItem> checkedCopy = new HashSet<>();
                checkedCopy.addAll(checked);
                checkedItemsMap.put(adapter.getFileType(), checkedCopy);
            }
        }
    }

    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(Constants.ACTION_FILE_ADDED_OR_REMOVED);
        filter.addAction(MusicPlaybackService.META_CHANGED);
        filter.addAction(MusicPlaybackService.PLAYSTATE_CHANGED);
        filter.addAction(MusicPlaybackService.SIMPLE_PLAYSTATE_STOPPED);
        getActivity().registerReceiver(broadcastReceiver, filter);
    }

    @Override
    public void onPause() {
        super.onPause();
        savePreviouslyCheckedFileDescriptors();
        MusicUtils.stopSimplePlayer();
        getActivity().unregisterReceiver(broadcastReceiver);
    }

    @Override
    public View getHeader(Activity activity) {
        LayoutInflater inflater = LayoutInflater.from(activity);
        header = inflater.inflate(R.layout.view_my_files_header, null, false);
        headerTitle = header.findViewById(R.id.view_my_files_header_text_title);
        headerTotal = header.findViewById(R.id.view_my_files_header_text_total);
        updateHeader();
        return header;
    }

    @Override
    public void onShow() {
    }

    @Override
    protected void initComponents(View v, Bundle savedInstanceState) {
        findView(v, R.id.fragment_my_files_select_all_container).setVisibility(View.GONE);
        findView(v, R.id.progressContainer).setVisibility(View.GONE);
        selectAllCheckboxListener = (buttonView, isChecked) -> onSelectAllChecked(isChecked);
        selectAllCheckbox = findView(v, R.id.fragment_my_files_select_all_checkbox);
        selectAllCheckboxContainer = findView(v, R.id.fragment_my_files_select_all_container);
        swipeRefresh = findView(v, R.id.fragment_my_files_swipe_refresh);
        swipeRefresh.setOnRefreshListener(() -> {
            long now = SystemClock.elapsedRealtime();
            if ((now - lastAdapterRefresh) > 5000) {
                refreshSelection();
            } else {
                swipeRefresh.setRefreshing(false);
            }
        });
        list = findView(v, R.id.fragment_my_files_gridview);
        list.setOnScrollListener(new ScrollListeners.FastScrollDisabledWhenIdleOnScrollListener());

        SwipeLayout swipe = findView(v, R.id.fragment_my_files_swipe);
        swipe.setOnSwipeListener(new SwipeLayout.OnSwipeListener() {
            @Override
            public void onSwipeLeft() {
                switchToThe(true);
            }

            @Override
            public void onSwipeRight() {
                switchToThe(false);
            }
        });

        tabLayout = findView(v, R.id.fragment_my_files_tab_layout_file_type);
        tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                tabClicked(tab.getPosition());
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {
                tabClicked(tab.getPosition());
            }
        });
    }

    private void tabClicked(int tabPosition) {
        byte fileType = tabPositionToFileType[tabPosition];
        lastFileType = fileType;
        if (adapter != null) {
            saveListViewVisiblePosition(adapter.getFileType());
            adapter.clear();
            adapter.clearChecked();
        }
        reloadFiles(fileType);
        refreshCheckBoxMenuItemVisibility();
        if (selectAllCheckbox != null) {
            selectAllModeOn = false;
            selectAllCheckbox.setChecked(false);
            selectAllCheckboxContainer.setVisibility(View.GONE);
        }
        logBrowseAction(fileType);
    }

    private void logBrowseAction(byte fileType) {
        try {
            UXStats.instance().log(browseUXActions[fileType]);
        } catch (Throwable ignored) {
        }
    }

    private void reloadFiles(byte fileType) {
        try {
            if (isAdded()) {
                LoaderManager loaderManager = getLoaderManager();
                loaderManager.destroyLoader(LOADER_FILES_ID);
                Bundle bundle = new Bundle();
                bundle.putByte("fileType", fileType);
                loaderManager.restartLoader(LOADER_FILES_ID, bundle, this);
            }
        } catch (Throwable t) {
            LOG.error(t.getMessage(), t);
        }
    }

    private Loader<Object> createLoaderFiles(final byte fileType) {
        CreateLoaderFilesAsyncTaskLoader loader = new CreateLoaderFilesAsyncTaskLoader(getActivity(), fileType);
        try {
            loader.forceLoad();
        } catch (Throwable t) {
            LOG.warn("createLoaderFiles(fileType=" + fileType + ") loader.forceLoad() failed. Continuing.", t);
        }
        return loader;
    }

    private final static class CreateLoaderFilesAsyncTaskLoader extends AsyncTaskLoader<Object> {

        private final byte fileType;

        CreateLoaderFilesAsyncTaskLoader(Context context, byte fileType) {
            super(context);
            this.fileType = fileType;
        }

        @Override
        public Object loadInBackground() {
            try {
                List<FileDescriptor> files = Librarian.instance().getFiles(getContext(), fileType, 0,
                        Integer.MAX_VALUE);
                return new Object[] { fileType, files };
            } catch (Throwable e) {
                LOG.error("Error performing finger", e);
            }
            return null;
        }
    }

    @Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        if (!hidden) {
            updateHeader();
        }
    }

    private void updateHeader() {
        if (!isVisible()) {
            return;
        }

        final byte fileType = adapter != null ? adapter.getFileType() : Constants.FILE_TYPE_AUDIO;
        if (isAdded() && isVisible()) {
            String fileTypeStr = getString(R.string.my_filetype,
                    UIUtils.getFileTypeAsString(getResources(), fileType));
            headerTitle.setText(fileTypeStr);
            headerTotal.setText("");
        }

        async(this, (f, ft) -> Librarian.instance().getNumFiles(f.getActivity(), ft), fileType,
                MyFilesFragment::updateHeaderPostTask);
    }

    private void updateHeaderPostTask(byte fileType, int numFiles) {
        if (header != null && fileType == lastFileType) {
            headerTotal.setText("(" + String.valueOf(numFiles) + ")");
        }
        if (adapter == null) {
            clickFileTypeTab(lastFileType);
        }
        refreshCheckBoxMenuItemVisibility();
        MusicUtils.stopSimplePlayer();
        restoreListViewScrollPosition();
    }

    private void clickFileTypeTab(byte lastFileType) {
        TabLayout.Tab tab = tabLayout.getTabAt(fileTypeToTabPosition[lastFileType]);
        if (tab != null) {
            tab.select();
        }
    }

    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 || data.length < 2 || data[1] == null) {
            LOG.warn("Something wrong, data is null");
            return;
        }
        try {
            byte fileType = (Byte) data[0];
            @SuppressWarnings("unchecked")
            List<FileDescriptor> items = (List<FileDescriptor>) data[1];
            adapter = new FileListAdapter(getActivity(), items, fileType, selectAllModeOn) {
                @Override
                protected void onLocalPlay() {
                    if (adapter != null) {
                        saveListViewVisiblePosition(adapter.getFileType());
                    }
                }

                @Override
                protected void onItemChecked(View v, boolean isChecked) {
                    super.onItemChecked(v, isChecked);
                    autoCheckUnCheckSelectAllCheckbox();
                    selectionModeCallback.onItemChecked(getActivity(), adapter.getCheckedCount());
                }

                @Override
                protected boolean onItemLongClicked(View v) {
                    return onFileItemLongClicked(v);
                }

                @Override
                protected void onItemClicked(View v) {
                    onFileItemClicked(v);
                }
            };
            adapter.setCheckboxesVisibility(selectAllModeOn);
            list.setNumColumns(adapter.getNumColumns());
            restorePreviouslyChecked();
            if (previousFilter != null) {
                performFilter(previousFilter);
            } else {
                updateAdapter();
            }
        } catch (Throwable e) {
            LOG.error("Error updating files in list", e);
        }
    }

    private void onToolbarMenuSelectModeCheckboxClick() {
        selectAllModeOn = !selectAllModeOn;
        enableSelectAllMode(selectAllModeOn, selectAllModeOn);
        if (selectAllModeOn) {
            selectionModeCallback.onItemChecked(getActivity(), adapter.getCheckedCount());
        }
    }

    private void enableSelectAllMode(boolean selectAll, boolean autoCheckAll) {
        selectAllModeOn = selectAll;
        selectAllCheckboxContainer
                .setVisibility(selectAllModeOn && adapter.getCount() > 0 ? View.VISIBLE : View.GONE);
        adapter.setSelectAllMode(selectAllModeOn);
        adapter.setCheckboxesVisibility(selectAllModeOn);
        adapter.setShowMenuOnClick(!selectAll);
        selectAllCheckbox.setChecked(autoCheckAll);
        swipeRefresh.setEnabled(!selectAll);
        if (selectAllModeOn) {
            startActionMode(selectionModeCallback);
        } else {
            adapter.clearChecked();
        }
        tabLayout.setVisibility(!selectAllModeOn ? View.VISIBLE : View.GONE);
    }

    private void autoCheckUnCheckSelectAllCheckbox() {
        selectAllCheckbox.setOnCheckedChangeListener(null);
        if (selectAllModeOn) {
            boolean allChecked = adapter.getCheckedCount() == adapter.getCount();
            selectAllCheckbox.setChecked(allChecked);
            selectAllCheckbox.setText(allChecked ? R.string.deselect_all : R.string.select_all);
        }
        selectAllCheckbox.setOnCheckedChangeListener(selectAllCheckboxListener);
    }

    private void onSelectAllChecked(boolean isChecked) {
        selectAllCheckbox.setText(isChecked ? R.string.deselect_all : R.string.select_all);
        if (isChecked) {
            adapter.checkAll();
        } else {
            adapter.clearChecked();
        }
        selectionModeCallback.onItemChecked(getActivity(), isChecked ? adapter.getCount() : 0);
    }

    private void onFileItemClicked(View v) {
        if (adapter == null || adapter.getFileType() == Constants.FILE_TYPE_RINGTONES) {
            return;
        }
        if (selectAllModeOn) {
            int position = adapter.getViewPosition(v);
            if (position == -1) {
                return;
            }
            Set<FileListAdapter.FileDescriptorItem> checked = adapter.getChecked();
            boolean wasChecked = checked.contains(v.getTag());
            adapter.setChecked(position, !wasChecked);
            adapter.notifyDataSetInvalidated();
            autoCheckUnCheckSelectAllCheckbox();
            selectionModeCallback.onItemChecked(getActivity(), adapter.getCheckedCount());
        }
    }

    private boolean onFileItemLongClicked(View v) {
        if (adapter == null || adapter.getFileType() == Constants.FILE_TYPE_RINGTONES) {
            return false;
        }
        int position = adapter.getViewPosition(v);
        if (position == -1) {
            return false;
        }
        enableSelectAllMode(!selectAllModeOn, false);
        onSelectAllChecked(false);
        adapter.setChecked(position, selectAllModeOn);
        if (selectAllModeOn) {
            selectionModeCallback.onItemChecked(getActivity(), 1);
        } else {
            selectionModeCallback.onDestroyActionMode(null);
        }
        return true;
    }

    private void updateAdapter() {
        list.setAdapter(adapter);
        refreshCheckBoxMenuItemVisibility();
        restoreListViewScrollPosition();
    }

    private void performFilter(String filterString) {
        this.previousFilter = filterString;
        if (adapter != null && filterString != null) {
            adapter.getFilter().filter(filterString, i -> updateAdapter());
        }
    }

    private void saveListViewVisiblePosition(byte fileType) {
        int firstVisiblePosition = list.getFirstVisiblePosition();
        ConfigurationManager.instance().setInt(
                Constants.MY_FILES_FRAGMENT_LISTVIEW_FIRST_VISIBLE_POSITION + fileType, firstVisiblePosition);
    }

    private int getSavedListViewVisiblePosition(byte fileType) {
        //will return 0 if not found.
        return ConfigurationManager.instance()
                .getInt(Constants.MY_FILES_FRAGMENT_LISTVIEW_FIRST_VISIBLE_POSITION + fileType);
    }

    private void switchToThe(boolean right) {
        int currentTabPosition = tabLayout.getSelectedTabPosition();
        int nextTabPosition = (right ? ++currentTabPosition : --currentTabPosition) % 6;
        if (nextTabPosition == -1) {
            nextTabPosition = 5;
        }
        TabLayout.Tab tab = tabLayout.getTabAt(nextTabPosition);
        if (tab != null) {
            tab.select();
        }
    }

    private void refreshCheckBoxMenuItemVisibility() {
        if (checkBoxMenuItem != null) {
            checkBoxMenuItem.setVisible(lastFileType != Constants.FILE_TYPE_RINGTONES && list.getCount() > 0);
        }
    }

    private class MyFilesActionModeCallback implements android.support.v7.view.ActionMode.Callback {
        private ActionMode mode;
        private Menu menu;
        private int numChecked;

        public void onItemChecked(Context context, int numChecked) {
            this.numChecked = numChecked;
            if (mode != null) {
                mode.setTitle(numChecked + " " + context.getString(R.string.selected));
                mode.invalidate();
            }
        }

        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            this.mode = mode;
            this.menu = menu;
            mode.getMenuInflater().inflate(R.menu.fragment_my_files_action_mode_menu, menu);
            return true;
        }

        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            if (numChecked == 0) {
                hideAllMenuActions();
            } else if (numChecked > 0) {
                FileListAdapter.FileDescriptorItem[] fileDescriptorItems = adapter.getChecked()
                        .toArray(new FileListAdapter.FileDescriptorItem[0]);
                if (fileDescriptorItems.length > 0) {
                    updateMenuActionsVisibility(fileDescriptorItems[0]);
                }
            }
            return true;
        }

        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            Activity context = getActivity();
            FileListAdapter.FileDescriptorItem[] fileDescriptorItems = adapter.getChecked()
                    .toArray(new FileListAdapter.FileDescriptorItem[0]);
            if (fileDescriptorItems.length == 0) {
                return false;
            }
            List<FileDescriptor> fileDescriptors = new ArrayList<>(fileDescriptorItems.length);
            for (FileListAdapter.FileDescriptorItem fileDescriptorItem : fileDescriptorItems) {
                fileDescriptors.add(fileDescriptorItem.fd);
            }
            FileListAdapter.FileDescriptorItem fileDescriptorItem = fileDescriptorItems[0];
            final FileDescriptor fd = fileDescriptorItem.fd;
            switch (item.getItemId()) {
            case R.id.fragment_my_files_action_mode_menu_delete:
                new DeleteAdapterFilesMenuAction(context, adapter, fileDescriptors, null).onClick();
                break;
            case R.id.fragment_my_files_action_mode_menu_seed:
                new SeedAction(context, fd, null).onClick();
                break;
            case R.id.fragment_my_files_action_mode_menu_open:
                new OpenMenuAction(context, fd, adapter.getItemPosition(fileDescriptorItem)).onClick();
                break;
            case R.id.fragment_my_files_action_mode_menu_file_information:
                new FileInformationAction(context, fd).onClick();
                break;
            case R.id.fragment_my_files_action_mode_menu_use_as_ringtone:
                new SetAsRingtoneMenuAction(context, fd).onClick();
                break;
            case R.id.fragment_my_files_action_mode_menu_use_as_wallpaper:
                new SetAsWallpaperMenuAction(context, fd).onClick();
                break;
            case R.id.fragment_my_files_action_mode_menu_copy_magnet:
                new CopyMagnetMenuAction(context, R.drawable.contextmenu_icon_magnet,
                        R.string.transfers_context_menu_copy_magnet,
                        R.string.transfers_context_menu_copy_magnet_copied, fd.filePath).onClick();
                break;
            case R.id.fragment_my_files_action_mode_menu_copy_info_hash:
                new CopyMagnetMenuAction(context, R.drawable.contextmenu_icon_copy,
                        R.string.transfers_context_menu_copy_infohash,
                        R.string.transfers_context_menu_copy_infohash_copied, fd.filePath, false).onClick();
                break;
            case R.id.fragment_my_files_action_mode_menu_rename:
                new RenameFileMenuAction(context, adapter, fd).onClick();
                break;
            case R.id.fragment_my_files_action_mode_menu_add_to_playlist:
                new AddToPlaylistMenuAction(context, fileDescriptors).onClick();
                break;
            case R.id.fragment_my_files_action_mode_menu_share:
                new SendFileMenuAction(context, fd).onClick();
                break;
            }
            enableSelectAllMode(false, false);
            mode.finish();
            return true;
        }

        @Override
        public void onDestroyActionMode(ActionMode mode) {
            enableSelectAllMode(false, false);
            this.mode.finish();
        }

        private void hideAllMenuActions() {
            if (menu != null && menu.size() > 0) {
                for (int i = 0; i < menu.size(); i++) {
                    menu.getItem(i).setVisible(false);
                }
            }
        }

        private void updateMenuActionsVisibility(FileListAdapter.FileDescriptorItem selectedFileDescriptor) {
            List<Integer> actionsToHide = new ArrayList<>();
            FileDescriptor fd = selectedFileDescriptor.fd;
            boolean canOpenFile = fd.mime != null
                    && (fd.mime.contains("audio") || fd.mime.contains("bittorrent") || fd.filePath != null);
            if (numChecked > 1) {
                actionsToHide.add(R.id.fragment_my_files_action_mode_menu_seed);
                actionsToHide.add(R.id.fragment_my_files_action_mode_menu_open);
                actionsToHide.add(R.id.fragment_my_files_action_mode_menu_file_information);
                actionsToHide.add(R.id.fragment_my_files_action_mode_menu_use_as_ringtone);
                actionsToHide.add(R.id.fragment_my_files_action_mode_menu_use_as_wallpaper);
                actionsToHide.add(R.id.fragment_my_files_action_mode_menu_rename);
                actionsToHide.add(R.id.fragment_my_files_action_mode_menu_copy_magnet);
                actionsToHide.add(R.id.fragment_my_files_action_mode_menu_copy_info_hash);
                actionsToHide.add(R.id.fragment_my_files_action_mode_menu_share);
            } else {
                if (numChecked == 1) {
                    if (!canOpenFile) {
                        actionsToHide.add(R.id.fragment_my_files_action_mode_menu_open);
                    }
                    if (fd.fileType != Constants.FILE_TYPE_AUDIO) {
                        actionsToHide.add(R.id.fragment_my_files_action_mode_menu_use_as_ringtone);
                    }
                    if (fd.fileType != Constants.FILE_TYPE_PICTURES) {
                        actionsToHide.add(R.id.fragment_my_files_action_mode_menu_use_as_wallpaper);
                    }
                    if (fd.fileType == Constants.FILE_TYPE_APPLICATIONS) {
                        actionsToHide.add(R.id.fragment_my_files_action_mode_menu_rename);
                        actionsToHide.add(R.id.fragment_my_files_action_mode_menu_share);
                        actionsToHide.add(R.id.fragment_my_files_action_mode_menu_delete);
                    }
                    if (fd.mime != null && !fd.mime.equals(Constants.MIME_TYPE_BITTORRENT)) {
                        actionsToHide.add(R.id.fragment_my_files_action_mode_menu_copy_magnet);
                        actionsToHide.add(R.id.fragment_my_files_action_mode_menu_copy_info_hash);
                    }
                }
            }
            if (fd.filePath != null && AndroidPlatform.saf(new File(fd.filePath))) {
                actionsToHide.add(R.id.fragment_my_files_action_mode_menu_seed);
            }
            if (fd.fileType != Constants.FILE_TYPE_AUDIO) {
                actionsToHide.add(R.id.fragment_my_files_action_mode_menu_add_to_playlist);
            }
            if (menu != null && menu.size() > 0) {
                for (int i = 0; i < menu.size(); i++) {
                    MenuItem item = menu.getItem(i);
                    item.setVisible(!actionsToHide.contains(item.getItemId()));
                }
            }
        }
    }

    private final class LocalBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (context instanceof Activity) {
                ((Activity) context).invalidateOptionsMenu();
            }
            if (action == null) {
                LOG.info("LocalBroadcastReceiver.onReceive() aborted. Intent had no action.");
                return;
            }
            if (action.equals(Constants.ACTION_MEDIA_PLAYER_PLAY)
                    || action.equals(Constants.ACTION_MEDIA_PLAYER_STOPPED)
                    || action.equals(Constants.ACTION_MEDIA_PLAYER_PAUSED)
                    || action.equals(MusicPlaybackService.PLAYSTATE_CHANGED)
                    || action.equals(MusicPlaybackService.META_CHANGED)
                    || action.equals(MusicPlaybackService.SIMPLE_PLAYSTATE_STOPPED)) {
                if (adapter != null) {
                    adapter.notifyDataSetChanged();
                }
            }
            if (action.equals(Constants.ACTION_FILE_ADDED_OR_REMOVED)) {
                if (intent.hasExtra(Constants.EXTRA_REFRESH_FILE_TYPE)) {
                    reloadFiles(intent.getByteExtra(Constants.EXTRA_REFRESH_FILE_TYPE, Constants.FILE_TYPE_AUDIO));
                } else {
                    // reload everything
                    reloadFiles(Constants.FILE_TYPE_APPLICATIONS);
                    reloadFiles(Constants.FILE_TYPE_RINGTONES);
                    reloadFiles(Constants.FILE_TYPE_TORRENTS);
                    reloadFiles(Constants.FILE_TYPE_PICTURES);
                    reloadFiles(Constants.FILE_TYPE_VIDEOS);
                    reloadFiles(Constants.FILE_TYPE_AUDIO);
                }
            }
        }
    }

    private void refreshSelection() {
        if (adapter != null) {
            lastAdapterRefresh = SystemClock.elapsedRealtime();
            clickFileTypeTab(adapter.getFileType());
        }
    }
}