<commit_before>/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

package com.nononsenseapps.filepicker;

import android.app.Activity;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.util.SortedList;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
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.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import static com.nononsenseapps.filepicker.Utils.isValidFileName;

/**
 * A fragment representing a list of Files.
 * <p/>
 * <p/>
 * Activities containing this fragment MUST implement the {@link
 * OnFilePickedListener}
 * interface.
 */
public abstract class AbstractFilePickerFragment<T> extends Fragment
        implements LoaderManager.LoaderCallbacks<SortedList<T>>,
        NewItemFragment.OnNewFolderListener, LogicHandler<T> {

    // The different preset modes of operation. This impacts the behaviour
    // and possible actions in the UI.
    public static final int MODE_FILE = 0;
    public static final int MODE_DIR = 1;
    public static final int MODE_FILE_AND_DIR = 2;
    public static final int MODE_NEW_FILE = 3;
    // Where to display on open.
    public static final String KEY_START_PATH = "KEY_START_PATH";
    // See MODE_XXX constants above for possible values
    public static final String KEY_MODE = "KEY_MODE";
    // If it should be possible to create directories.
    public static final String KEY_ALLOW_DIR_CREATE = "KEY_ALLOW_DIR_CREATE";
    // Allow multiple items to be selected.
    public static final String KEY_ALLOW_MULTIPLE = "KEY_ALLOW_MULTIPLE";
    // Allow an existing file to be selected under MODE_NEW_FILE
    public static final String KEY_ALLOW_EXISTING_FILE = "KEY_ALLOW_EXISTING_FILE";
    // Used for saving state.
    protected static final String KEY_CURRENT_PATH = "KEY_CURRENT_PATH";
    protected final HashSet<T> mCheckedItems;
    protected final HashSet<CheckableViewHolder> mCheckedVisibleViewHolders;
    protected int mode = MODE_FILE;
    protected T mCurrentPath = null;
    protected boolean allowCreateDir = false;
    protected boolean allowMultiple = false;
    protected boolean allowExistingFile = true;
    protected OnFilePickedListener mListener;
    protected FileItemAdapter<T> mAdapter = null;
    protected TextView mCurrentDirView;
    protected EditText mEditTextFileName;
    protected SortedList<T> mFiles = null;
    protected Toast mToast = null;
    // Keep track if we are currently loading a directory, in case it takes a long time
    protected boolean isLoading = false;
    private View mNewFileButtonContainer = null;
    private View mRegularButtonContainer = null;

    /**
     * Mandatory empty constructor for the fragment manager to instantiate the
     * fragment (e.g. upon screen orientation changes).
     */
    public AbstractFilePickerFragment() {
        mCheckedItems = new HashSet<>();
        mCheckedVisibleViewHolders = new HashSet<>();

        // Retain this fragment across configuration changes, to allow
        // asynctasks and such to be used with ease.
        setRetainInstance(true);
    }

    protected FileItemAdapter<T> getAdapter() {
        return mAdapter;
    }

    protected FileItemAdapter<T> getDummyAdapter() {
        return new FileItemAdapter<>(this);
    }

    /**
     * Set before making the fragment visible. This method will re-use the existing
     * arguments bundle in the fragment if it exists so extra arguments will not
     * be overwritten. This allows you to set any extra arguments in the fragment
     * constructor if you wish.
     * <p/>
     * The key/value-pairs listed below will be overwritten however.
     *
     * @param startPath      path to directory the picker will show upon start
     * @param mode           what is allowed to be selected (dirs, files, both)
     * @param allowMultiple  selecting a single item or several?
     * @param allowDirCreate can new directories be created?
     */
    public void setArgs(@Nullable final String startPath, final int mode,
                        final boolean allowMultiple, final boolean allowDirCreate,
                        final boolean allowExistingFile) {
        // Validate some assumptions so users don't get surprised (or get surprised early)
        if (mode == MODE_NEW_FILE && allowMultiple) {
            throw new IllegalArgumentException(
                    "MODE_NEW_FILE does not support 'allowMultiple'");

        }
        // There might have been arguments set elsewhere, if so do not overwrite them.
        Bundle b = getArguments();
        if (b == null) {
            b = new Bundle();
        }

        if (startPath != null) {
            b.putString(KEY_START_PATH, startPath);
        }
        b.putBoolean(KEY_ALLOW_DIR_CREATE, allowDirCreate);
        b.putBoolean(KEY_ALLOW_MULTIPLE, allowMultiple);
        b.putBoolean(KEY_ALLOW_EXISTING_FILE, allowExistingFile);
        b.putInt(KEY_MODE, mode);
        setArguments(b);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.nnf_fragment_filepicker, container, false);

        Toolbar toolbar = (Toolbar) view.findViewById(R.id.nnf_picker_toolbar);
        if (toolbar != null) {
            setupToolbar(toolbar);
        }

        RecyclerView recyclerView = (RecyclerView) view.findViewById(android.R.id.list);
        // improve performance if you know that changes in content
        // do not change the size of the RecyclerView
        recyclerView.setHasFixedSize(true);
        // use a linear layout manager
        LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
        recyclerView.setLayoutManager(layoutManager);
        // Set adapter
        mAdapter = new FileItemAdapter<>(this);
        recyclerView.setAdapter(mAdapter);

        view.findViewById(R.id.nnf_button_cancel)
                .setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(final View v) {
                        onClickCancel(v);
                    }
                });

        view.findViewById(R.id.nnf_button_ok).setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(final View v) {
                        onClickOk(v);
                    }
                });
        view.findViewById(R.id.nnf_button_ok_newfile).setOnClickListener(
                new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        onClickOk(v);
                    }
                });

        mNewFileButtonContainer = view.findViewById(R.id.nnf_newfile_button_container);
        mRegularButtonContainer = view.findViewById(R.id.nnf_button_container);

        mEditTextFileName = (EditText) view.findViewById(R.id.nnf_text_filename);

        mCurrentDirView = (TextView) view.findViewById(R.id.nnf_current_dir);
        // Restore state
        if (mCurrentPath != null && mCurrentDirView != null) {
            mCurrentDirView.setText(getFullPath(mCurrentPath));<commit_msg>If the name is changed, deselect items<commit_after>/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

package com.nononsenseapps.filepicker;

import android.app.Activity;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.util.SortedList;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.text.Editable;
import android.text.TextWatcher;
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.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import static com.nononsenseapps.filepicker.Utils.isValidFileName;

/**
 * A fragment representing a list of Files.
 * <p/>
 * <p/>
 * Activities containing this fragment MUST implement the {@link
 * OnFilePickedListener}
 * interface.
 */
public abstract class AbstractFilePickerFragment<T> extends Fragment
        implements LoaderManager.LoaderCallbacks<SortedList<T>>,
        NewItemFragment.OnNewFolderListener, LogicHandler<T> {

    // The different preset modes of operation. This impacts the behaviour
    // and possible actions in the UI.
    public static final int MODE_FILE = 0;
    public static final int MODE_DIR = 1;
    public static final int MODE_FILE_AND_DIR = 2;
    public static final int MODE_NEW_FILE = 3;
    // Where to display on open.
    public static final String KEY_START_PATH = "KEY_START_PATH";
    // See MODE_XXX constants above for possible values
    public static final String KEY_MODE = "KEY_MODE";
    // If it should be possible to create directories.
    public static final String KEY_ALLOW_DIR_CREATE = "KEY_ALLOW_DIR_CREATE";
    // Allow multiple items to be selected.
    public static final String KEY_ALLOW_MULTIPLE = "KEY_ALLOW_MULTIPLE";
    // Allow an existing file to be selected under MODE_NEW_FILE
    public static final String KEY_ALLOW_EXISTING_FILE = "KEY_ALLOW_EXISTING_FILE";
    // Used for saving state.
    protected static final String KEY_CURRENT_PATH = "KEY_CURRENT_PATH";
    protected final HashSet<T> mCheckedItems;
    protected final HashSet<CheckableViewHolder> mCheckedVisibleViewHolders;
    protected int mode = MODE_FILE;
    protected T mCurrentPath = null;
    protected boolean allowCreateDir = false;
    protected boolean allowMultiple = false;
    protected boolean allowExistingFile = true;
    protected OnFilePickedListener mListener;
    protected FileItemAdapter<T> mAdapter = null;
    protected TextView mCurrentDirView;
    protected EditText mEditTextFileName;
    protected SortedList<T> mFiles = null;
    protected Toast mToast = null;
    // Keep track if we are currently loading a directory, in case it takes a long time
    protected boolean isLoading = false;
    private View mNewFileButtonContainer = null;
    private View mRegularButtonContainer = null;

    /**
     * Mandatory empty constructor for the fragment manager to instantiate the
     * fragment (e.g. upon screen orientation changes).
     */
    public AbstractFilePickerFragment() {
        mCheckedItems = new HashSet<>();
        mCheckedVisibleViewHolders = new HashSet<>();

        // Retain this fragment across configuration changes, to allow
        // asynctasks and such to be used with ease.
        setRetainInstance(true);
    }

    protected FileItemAdapter<T> getAdapter() {
        return mAdapter;
    }

    protected FileItemAdapter<T> getDummyAdapter() {
        return new FileItemAdapter<>(this);
    }

    /**
     * Set before making the fragment visible. This method will re-use the existing
     * arguments bundle in the fragment if it exists so extra arguments will not
     * be overwritten. This allows you to set any extra arguments in the fragment
     * constructor if you wish.
     * <p/>
     * The key/value-pairs listed below will be overwritten however.
     *
     * @param startPath      path to directory the picker will show upon start
     * @param mode           what is allowed to be selected (dirs, files, both)
     * @param allowMultiple  selecting a single item or several?
     * @param allowDirCreate can new directories be created?
     */
    public void setArgs(@Nullable final String startPath, final int mode,
                        final boolean allowMultiple, final boolean allowDirCreate,
                        final boolean allowExistingFile) {
        // Validate some assumptions so users don't get surprised (or get surprised early)
        if (mode == MODE_NEW_FILE && allowMultiple) {
            throw new IllegalArgumentException(
                    "MODE_NEW_FILE does not support 'allowMultiple'");

        }
        // There might have been arguments set elsewhere, if so do not overwrite them.
        Bundle b = getArguments();
        if (b == null) {
            b = new Bundle();
        }

        if (startPath != null) {
            b.putString(KEY_START_PATH, startPath);
        }
        b.putBoolean(KEY_ALLOW_DIR_CREATE, allowDirCreate);
        b.putBoolean(KEY_ALLOW_MULTIPLE, allowMultiple);
        b.putBoolean(KEY_ALLOW_EXISTING_FILE, allowExistingFile);
        b.putInt(KEY_MODE, mode);
        setArguments(b);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.nnf_fragment_filepicker, container, false);

        Toolbar toolbar = (Toolbar) view.findViewById(R.id.nnf_picker_toolbar);
        if (toolbar != null) {
            setupToolbar(toolbar);
        }

        RecyclerView recyclerView = (RecyclerView) view.findViewById(android.R.id.list);
        // improve performance if you know that changes in content
        // do not change the size of the RecyclerView
        recyclerView.setHasFixedSize(true);
        // use a linear layout manager
        LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
        recyclerView.setLayoutManager(layoutManager);
        // Set adapter
        mAdapter = new FileItemAdapter<>(this);
        recyclerView.setAdapter(mAdapter);

        view.findViewById(R.id.nnf_button_cancel)
                .setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(final View v) {
                        onClickCancel(v);
                    }
                });

        view.findViewById(R.id.nnf_button_ok).setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(final View v) {
                        onClickOk(v);
                    }
                });
        view.findViewById(R.id.nnf_button_ok_newfile).setOnClickListener(
                new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        onClickOk(v);
                    }
                });

        mNewFileButtonContainer = view.findViewById(R.id.nnf_newfile_button_container);
        mRegularButtonContainer = view.findViewById(R.id.nnf_button_container);

        mEditTextFileName = (EditText) view.findViewById(R.id.nnf_text_filename);
        mEditTextFileName.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {
                // deSelect anything selected since the user just modified the name
                clearSelections();
            }
        });

        mCurrentDirView = (TextView) view.findViewById(R.id.nnf_current_dir);
        // Restore state
        if (mCurrentPath != null && mCurrentDirView != null) {
            mCurrentDirView.setText(getFullPath(mCurrentPath));
{'fraction_non_alphanumeric': 0.07633178405434395, 'fraction_numerical': 0.0019068049100226434, 'mean_word_length': 2.738694586767654, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7154403', 'n_tokens_mistral': 4502, 'n_tokens_neox': 4326, 'n_words': 1329}