Add a new mode for picking new files
This adds a new mode so the library can be used for "Save-As"-dialogs. Fixes #82
This commit is contained in:
parent
4080ad60c7
commit
c8978a9880
|
|
@ -36,10 +36,10 @@ import java.util.List;
|
|||
* If you want to be able to select multiple items, include EXTRA_ALLOW_MULTIPLE
|
||||
* (default false).
|
||||
* <p/>
|
||||
* Two non-standard extra arguments are supported as well: EXTRA_ONLY_DIRS
|
||||
* (defaults to false)
|
||||
* allows only directories to be selected.
|
||||
* And EXTRA_START_PATH (default null), which should specify the starting path.
|
||||
* Some non-standard extra arguments are supported as well:
|
||||
* EXTRA_ONLY_DIRS - (default false) allows only directories to be selected.
|
||||
* EXTRA_START_PATH - (default null) which should specify the starting path.
|
||||
* EXTRA_ALLOW_EXISTING_FILE - (default true) if existing files are selectable in 'new file'-mode
|
||||
* <p/>
|
||||
* The result of the user's action is returned in onActivityResult intent,
|
||||
* access it using getUri.
|
||||
|
|
@ -60,16 +60,20 @@ public abstract class AbstractFilePickerActivity<T> extends AppCompatActivity
|
|||
// For compatibility
|
||||
public static final String EXTRA_ALLOW_MULTIPLE =
|
||||
"android.intent.extra" + ".ALLOW_MULTIPLE";
|
||||
public static final String EXTRA_ALLOW_EXISTING_FILE =
|
||||
"android.intent.extra" + ".ALLOW_EXISTING_FILE";
|
||||
public static final String EXTRA_PATHS = "nononsense.intent.PATHS";
|
||||
public static final int MODE_FILE = AbstractFilePickerFragment.MODE_FILE;
|
||||
public static final int MODE_FILE_AND_DIR =
|
||||
AbstractFilePickerFragment.MODE_FILE_AND_DIR;
|
||||
public static final int MODE_NEW_FILE = AbstractFilePickerFragment.MODE_NEW_FILE;
|
||||
public static final int MODE_DIR = AbstractFilePickerFragment.MODE_DIR;
|
||||
protected static final String TAG = "filepicker_fragment";
|
||||
protected String startPath = null;
|
||||
protected int mode = AbstractFilePickerFragment.MODE_FILE;
|
||||
protected boolean allowCreateDir = false;
|
||||
protected boolean allowMultiple = false;
|
||||
private boolean allowExistingFile = true;
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
@ -86,6 +90,8 @@ public abstract class AbstractFilePickerActivity<T> extends AppCompatActivity
|
|||
allowCreateDir);
|
||||
allowMultiple =
|
||||
intent.getBooleanExtra(EXTRA_ALLOW_MULTIPLE, allowMultiple);
|
||||
allowExistingFile =
|
||||
intent.getBooleanExtra(EXTRA_ALLOW_EXISTING_FILE, allowExistingFile);
|
||||
}
|
||||
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
|
|
@ -94,7 +100,7 @@ public abstract class AbstractFilePickerActivity<T> extends AppCompatActivity
|
|||
|
||||
if (fragment == null) {
|
||||
fragment =
|
||||
getFragment(startPath, mode, allowMultiple, allowCreateDir);
|
||||
getFragment(startPath, mode, allowMultiple, allowCreateDir, allowExistingFile);
|
||||
}
|
||||
|
||||
if (fragment != null) {
|
||||
|
|
@ -108,7 +114,7 @@ public abstract class AbstractFilePickerActivity<T> extends AppCompatActivity
|
|||
|
||||
protected abstract AbstractFilePickerFragment<T> getFragment(
|
||||
@Nullable final String startPath, final int mode, final boolean allowMultiple,
|
||||
final boolean allowCreateDir);
|
||||
final boolean allowCreateDir, boolean allowExistingFile);
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle b) {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ 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;
|
||||
|
||||
|
|
@ -34,6 +35,8 @@ 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/>
|
||||
|
|
@ -51,6 +54,7 @@ public abstract class AbstractFilePickerFragment<T> extends Fragment
|
|||
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
|
||||
|
|
@ -59,6 +63,8 @@ public abstract class AbstractFilePickerFragment<T> extends Fragment
|
|||
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;
|
||||
|
|
@ -67,13 +73,17 @@ public abstract class AbstractFilePickerFragment<T> extends Fragment
|
|||
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
|
||||
|
|
@ -110,7 +120,14 @@ public abstract class AbstractFilePickerFragment<T> extends Fragment
|
|||
* @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 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) {
|
||||
|
|
@ -122,6 +139,7 @@ public abstract class AbstractFilePickerFragment<T> extends Fragment
|
|||
}
|
||||
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);
|
||||
}
|
||||
|
|
@ -129,7 +147,7 @@ public abstract class AbstractFilePickerFragment<T> extends Fragment
|
|||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.nnf_fragment_filepicker, container, false);
|
||||
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) {
|
||||
|
|
@ -155,13 +173,24 @@ public abstract class AbstractFilePickerFragment<T> extends Fragment
|
|||
}
|
||||
});
|
||||
|
||||
view.findViewById(R.id.nnf_button_ok)
|
||||
.setOnClickListener(new View.OnClickListener() {
|
||||
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
|
||||
|
|
@ -194,6 +223,12 @@ public abstract class AbstractFilePickerFragment<T> extends Fragment
|
|||
}
|
||||
|
||||
// Some invalid cases first
|
||||
if (MODE_NEW_FILE == mode && !isValidFileName(getNewFileName())) {
|
||||
mToast = Toast.makeText(getActivity(), R.string.nnf_need_valid_filename,
|
||||
Toast.LENGTH_SHORT);
|
||||
mToast.show();
|
||||
return;
|
||||
}
|
||||
if ((allowMultiple || mode == MODE_FILE) &&
|
||||
(mCheckedItems.isEmpty() || getFirstCheckedItem() == null)) {
|
||||
if (mToast == null) {
|
||||
|
|
@ -204,9 +239,16 @@ public abstract class AbstractFilePickerFragment<T> extends Fragment
|
|||
return;
|
||||
}
|
||||
|
||||
if (allowMultiple) {
|
||||
// New file allows only a single file
|
||||
if (mode == MODE_NEW_FILE) {
|
||||
mListener.onFilePicked(
|
||||
toUri(mCurrentPath).buildUpon()
|
||||
.appendPath(getNewFileName())
|
||||
.build());
|
||||
} else if (allowMultiple) {
|
||||
mListener.onFilesPicked(toUri(mCheckedItems));
|
||||
} else if (mode == MODE_FILE) {
|
||||
//noinspection ConstantConditions
|
||||
mListener.onFilePicked(toUri(getFirstCheckedItem()));
|
||||
} else if (mode == MODE_DIR) {
|
||||
mListener.onFilePicked(toUri(mCurrentPath));
|
||||
|
|
@ -220,6 +262,15 @@ public abstract class AbstractFilePickerFragment<T> extends Fragment
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return filename as entered/picked by the user for the new file
|
||||
*/
|
||||
@NonNull
|
||||
protected String getNewFileName() {
|
||||
return mEditTextFileName.getText().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the toolbar anyway you like here. Default is to set it as the activity's
|
||||
* main action bar. Override if you already provide an action bar.
|
||||
|
|
@ -254,7 +305,7 @@ public abstract class AbstractFilePickerFragment<T> extends Fragment
|
|||
(mode == MODE_FILE_AND_DIR && allowMultiple));
|
||||
} else {
|
||||
// File
|
||||
checkable = (mode != MODE_DIR);
|
||||
checkable = (mode == MODE_FILE || mode == MODE_FILE_AND_DIR || allowExistingFile);
|
||||
}
|
||||
return checkable;
|
||||
}
|
||||
|
|
@ -301,6 +352,8 @@ public abstract class AbstractFilePickerFragment<T> extends Fragment
|
|||
.getBoolean(KEY_ALLOW_DIR_CREATE, allowCreateDir);
|
||||
allowMultiple = savedInstanceState
|
||||
.getBoolean(KEY_ALLOW_MULTIPLE, allowMultiple);
|
||||
allowExistingFile = savedInstanceState
|
||||
.getBoolean(KEY_ALLOW_EXISTING_FILE, allowExistingFile);
|
||||
|
||||
String path = savedInstanceState.getString(KEY_CURRENT_PATH);
|
||||
if (path != null) {
|
||||
|
|
@ -312,6 +365,8 @@ public abstract class AbstractFilePickerFragment<T> extends Fragment
|
|||
.getBoolean(KEY_ALLOW_DIR_CREATE, allowCreateDir);
|
||||
allowMultiple = getArguments()
|
||||
.getBoolean(KEY_ALLOW_MULTIPLE, allowMultiple);
|
||||
allowExistingFile = getArguments()
|
||||
.getBoolean(KEY_ALLOW_EXISTING_FILE, allowExistingFile);
|
||||
if (getArguments().containsKey(KEY_START_PATH)) {
|
||||
String path = getArguments().getString(KEY_START_PATH);
|
||||
if (path != null) {
|
||||
|
|
@ -326,9 +381,20 @@ public abstract class AbstractFilePickerFragment<T> extends Fragment
|
|||
}
|
||||
}
|
||||
|
||||
setModeView();
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides/Shows appropriate views depending on mode
|
||||
*/
|
||||
protected void setModeView() {
|
||||
boolean nf = mode == MODE_NEW_FILE;
|
||||
mNewFileButtonContainer.setVisibility(nf ? View.VISIBLE : View.GONE);
|
||||
mRegularButtonContainer.setVisibility(nf ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.picker_actions, menu);
|
||||
|
|
@ -357,6 +423,7 @@ public abstract class AbstractFilePickerFragment<T> extends Fragment
|
|||
super.onSaveInstanceState(b);
|
||||
b.putString(KEY_CURRENT_PATH, mCurrentPath.toString());
|
||||
b.putBoolean(KEY_ALLOW_MULTIPLE, allowMultiple);
|
||||
b.putBoolean(KEY_ALLOW_EXISTING_FILE, allowExistingFile);
|
||||
b.putBoolean(KEY_ALLOW_DIR_CREATE, allowCreateDir);
|
||||
b.putInt(KEY_MODE, mode);
|
||||
}
|
||||
|
|
@ -623,7 +690,7 @@ public abstract class AbstractFilePickerFragment<T> extends Fragment
|
|||
/**
|
||||
* Called when a selectable item's checkbox is pressed. This should toggle its selected state.
|
||||
* Note that if only a single item can be selected, then other potentially selected views on
|
||||
* screen must be de-selected.
|
||||
* screen must be de-selected. The text box for new filename is also cleared.
|
||||
*
|
||||
* @param viewHolder for the item containing the checkbox.
|
||||
*/
|
||||
|
|
@ -632,9 +699,13 @@ public abstract class AbstractFilePickerFragment<T> extends Fragment
|
|||
viewHolder.checkbox.setChecked(false);
|
||||
mCheckedItems.remove(viewHolder.file);
|
||||
mCheckedVisibleViewHolders.remove(viewHolder);
|
||||
if (!allowMultiple) {
|
||||
mEditTextFileName.setText("");
|
||||
}
|
||||
} else {
|
||||
if (!allowMultiple) {
|
||||
clearSelections();
|
||||
mEditTextFileName.setText(getName(viewHolder.file));
|
||||
}
|
||||
viewHolder.checkbox.setChecked(true);
|
||||
mCheckedItems.add(viewHolder.file);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ package com.nononsenseapps.filepicker;
|
|||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Environment;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
|
|
@ -23,11 +22,11 @@ public class FilePickerActivity extends AbstractFilePickerActivity<File> {
|
|||
@Override
|
||||
protected AbstractFilePickerFragment<File> getFragment(
|
||||
@Nullable final String startPath, final int mode, final boolean allowMultiple,
|
||||
final boolean allowCreateDir) {
|
||||
final boolean allowCreateDir, final boolean allowExistingFile) {
|
||||
AbstractFilePickerFragment<File> fragment = new FilePickerFragment();
|
||||
// startPath is allowed to be null. In that case, default folder should be SD-card and not "/"
|
||||
fragment.setArgs(startPath != null ? startPath : Environment.getExternalStorageDirectory().getPath(),
|
||||
mode, allowMultiple, allowCreateDir);
|
||||
mode, allowMultiple, allowCreateDir, allowExistingFile);
|
||||
return fragment;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -325,7 +325,8 @@ public class FilePickerFragment extends AbstractFilePickerFragment<File> {
|
|||
if(!showHiddenItems && file.isHidden()){
|
||||
return false;
|
||||
}
|
||||
return (isDir(file) || (mode == MODE_FILE || mode == MODE_FILE_AND_DIR));
|
||||
return (isDir(file) ||
|
||||
(mode != MODE_DIR));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ package com.nononsenseapps.filepicker;
|
|||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import static com.nononsenseapps.filepicker.Utils.isValidFileName;
|
||||
|
||||
public class NewFolderFragment extends NewItemFragment {
|
||||
|
||||
|
|
@ -25,9 +26,6 @@ public class NewFolderFragment extends NewItemFragment {
|
|||
|
||||
@Override
|
||||
protected boolean validateName(@Nullable final String itemName) {
|
||||
return !TextUtils.isEmpty(itemName)
|
||||
&& !itemName.contains("/")
|
||||
&& !itemName.equals(".")
|
||||
&& !itemName.equals("..");
|
||||
return isValidFileName(itemName);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,12 +113,6 @@ public abstract class NewItemFragment extends DialogFragment {
|
|||
protected abstract boolean validateName(final String itemName);
|
||||
|
||||
public interface OnNewFolderListener {
|
||||
/**
|
||||
* Name is validated to be non-null, non-empty and not containing any
|
||||
* slashes.
|
||||
*
|
||||
* @param name The name of the folder the user wishes to create.
|
||||
*/
|
||||
void onNewFolder(@NonNull final String name);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
package com.nononsenseapps.filepicker;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
/**
|
||||
* Some utility methods
|
||||
*/
|
||||
public class Utils {
|
||||
|
||||
/**
|
||||
* Name is validated to be non-null, non-empty and not containing any
|
||||
* slashes.
|
||||
*
|
||||
* @param name The name of the folder the user wishes to create.
|
||||
*/
|
||||
public static boolean isValidFileName(@Nullable String name) {
|
||||
return !TextUtils.isEmpty(name)
|
||||
&& !name.contains("/")
|
||||
&& !name.equals(".")
|
||||
&& !name.equals("..");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z"/>
|
||||
</vector>
|
||||
|
|
@ -34,41 +34,74 @@
|
|||
android:id="@android:id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@+id/nnf_button_container"
|
||||
android:layout_above="@+id/nnf_buttons_container"
|
||||
android:layout_below="@+id/nnf_picker_toolbar"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:focusable="true"
|
||||
tools:listitem="@layout/nnf_filepicker_listitem_dir"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/nnf_button_container"
|
||||
<FrameLayout
|
||||
android:id="@+id/nnf_buttons_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:orientation="horizontal">
|
||||
android:layout_alignParentBottom="true">
|
||||
|
||||
<Button
|
||||
android:id="@+id/nnf_button_cancel"
|
||||
style="?attr/borderlessButtonStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@android:string/cancel"/>
|
||||
<LinearLayout
|
||||
android:id="@+id/nnf_button_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/nnf_button_ok"
|
||||
style="?attr/borderlessButtonStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@android:string/ok"/>
|
||||
<Button
|
||||
android:id="@+id/nnf_button_cancel"
|
||||
style="?attr/borderlessButtonStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@android:string/cancel"/>
|
||||
|
||||
</LinearLayout>
|
||||
<Button
|
||||
android:id="@+id/nnf_button_ok"
|
||||
style="?attr/borderlessButtonStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@android:string/ok"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/nnf_newfile_button_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/nnf_text_filename"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_weight="1"
|
||||
android:hint="@string/nnf_filename"
|
||||
android:maxLines="1"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="8dp"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/nnf_button_ok_newfile"
|
||||
style="?attr/borderlessButtonStyle"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:hint="@android:string/ok"
|
||||
android:src="@drawable/nnf_ic_save_black_24dp"
|
||||
android:tint="?attr/nnf_save_icon_color"/>
|
||||
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_above="@id/nnf_button_container"
|
||||
android:layout_above="@id/nnf_buttons_container"
|
||||
android:background="?nnf_separator_color"/>
|
||||
</RelativeLayout>
|
||||
|
|
@ -11,6 +11,8 @@
|
|||
<attr name="nnf_toolbarTheme" format="reference"/>
|
||||
<!-- Color to apply to separator for OK/Cancel buttons -->
|
||||
<attr name="nnf_separator_color" format="color"/>
|
||||
<!-- Color to apply to the save icon -->
|
||||
<attr name="nnf_save_icon_color" format="color"/>
|
||||
<!-- Color to apply to the directory icons in list -->
|
||||
<attr name="nnf_dir_icon_color" format="color"/>
|
||||
</resources>
|
||||
|
|
@ -9,5 +9,7 @@
|
|||
<string name="nnf_create_folder_error">Failed to create folder</string>
|
||||
<string name="nnf_name">Name</string>
|
||||
<string name="nnf_select_something_first">Please select something first</string>
|
||||
<string name="nnf_need_valid_filename">Please enter or select a valid filename</string>
|
||||
<string name="nnf_permission_external_write_denied">Permisson to access filesystem denied</string>
|
||||
<string name="nnf_filename">Filename</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
<!-- You can override this in your sub theme -->
|
||||
<item name="nnf_toolbarTheme">@style/ThemeOverlay.AppCompat.ActionBar</item>
|
||||
<item name="nnf_separator_color">@color/nnf_dark_separator_color</item>
|
||||
<item name="nnf_save_icon_color">?attr/colorAccent</item>
|
||||
<item name="nnf_dir_icon_color">?attr/colorAccent</item>
|
||||
|
||||
<!-- These are important. Handled by toolbar. -->
|
||||
|
|
@ -23,6 +24,7 @@
|
|||
<!-- You can override this in your sub theme -->
|
||||
<item name="nnf_toolbarTheme">@style/ThemeOverlay.AppCompat.ActionBar</item>
|
||||
<item name="nnf_separator_color">@color/nnf_light_separator_color</item>
|
||||
<item name="nnf_save_icon_color">?attr/colorAccent</item>
|
||||
<item name="nnf_dir_icon_color">?attr/colorAccent</item>
|
||||
|
||||
<!-- These are important. Handled by toolbar. -->
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ import org.junit.Test;
|
|||
|
||||
import java.io.File;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
|
||||
public class FilePickerFragmentTest {
|
||||
|
|
@ -62,6 +64,17 @@ public class FilePickerFragmentTest {
|
|||
assertEquals("/", fragment.getRoot().getPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetArgsMultipleNewFiles() throws Exception {
|
||||
try {
|
||||
fragment.setArgs(null, AbstractFilePickerFragment.MODE_NEW_FILE, true, false, true);
|
||||
fail("Expected exception");
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertTrue("Should mention the mode limitations: " + e.getMessage(),
|
||||
e.getMessage().contains("MODE_NEW_FILE"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompareFiles() throws Exception {
|
||||
assertEquals(0, fragment.compareFiles(new File("/A/A"), new File("/A/A")));
|
||||
|
|
|
|||
|
|
@ -24,12 +24,13 @@ android {
|
|||
targetSdkVersion 23
|
||||
versionCode gitCommitCount
|
||||
versionName gitTag
|
||||
archivesBaseName = "nononsensefilepicker-sample-${gitTag}".toString()
|
||||
//archivesBaseName = "nononsensefilepicker-sample-${gitTag}".toString()
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
release {
|
||||
archivesBaseName = "nononsensefilepicker-sample-${gitTag}".toString()
|
||||
//minifyEnabled true
|
||||
//shrinkResources true
|
||||
//proguardFiles getDefaultProguardFile('proguard-android.txt')
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
package com.nononsenseapps.filepicker.sample;
|
||||
|
||||
import android.os.Environment;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerActivity;
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerFragment;
|
||||
|
|
@ -25,12 +25,12 @@ public class MultimediaPickerActivity extends AbstractFilePickerActivity {
|
|||
|
||||
@Override
|
||||
protected AbstractFilePickerFragment<File> getFragment(
|
||||
@NonNull final String startPath, final int mode, final boolean allowMultiple,
|
||||
final boolean allowCreateDir) {
|
||||
@Nullable final String startPath, final int mode, final boolean allowMultiple,
|
||||
final boolean allowCreateDir, boolean allowExistingFile) {
|
||||
AbstractFilePickerFragment<File> fragment = new MultimediaPickerFragment();
|
||||
// startPath is allowed to be null. In that case, default folder should be SD-card and not "/"
|
||||
fragment.setArgs(startPath != null ? startPath : Environment.getExternalStorageDirectory().getPath(),
|
||||
mode, allowMultiple, allowCreateDir);
|
||||
mode, allowMultiple, allowCreateDir, allowExistingFile);
|
||||
return fragment;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import android.view.View;
|
|||
import android.widget.CheckBox;
|
||||
import android.widget.RadioGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.dropbox.client2.DropboxAPI;
|
||||
import com.dropbox.client2.android.AndroidAuthSession;
|
||||
|
|
@ -50,6 +51,8 @@ public class NoNonsenseFilePicker extends Activity {
|
|||
(CheckBox) findViewById(R.id.checkAllowCreateDir);
|
||||
final CheckBox checkAllowMultiple =
|
||||
(CheckBox) findViewById(R.id.checkAllowMultiple);
|
||||
final CheckBox checkAllowExistingFile =
|
||||
(CheckBox) findViewById(R.id.checkAllowExistingFile);
|
||||
final CheckBox checkLightTheme =
|
||||
(CheckBox) findViewById(R.id.checkLightTheme);
|
||||
final RadioGroup radioGroup =
|
||||
|
|
@ -75,26 +78,38 @@ public class NoNonsenseFilePicker extends Activity {
|
|||
checkAllowMultiple.isChecked());
|
||||
i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR,
|
||||
checkAllowCreateDir.isChecked());
|
||||
i.putExtra(FilePickerActivity.EXTRA_ALLOW_EXISTING_FILE,
|
||||
checkAllowExistingFile.isChecked());
|
||||
|
||||
// What mode is selected
|
||||
final int mode;
|
||||
switch (radioGroup.getCheckedRadioButtonId()) {
|
||||
case R.id.radioDir:
|
||||
mode = AbstractFilePickerFragment.MODE_DIR;
|
||||
mode = FilePickerActivity.MODE_DIR;
|
||||
break;
|
||||
case R.id.radioFilesAndDirs:
|
||||
mode =
|
||||
AbstractFilePickerFragment.MODE_FILE_AND_DIR;
|
||||
mode = FilePickerActivity.MODE_FILE_AND_DIR;
|
||||
break;
|
||||
case R.id.radioNewFile:
|
||||
mode = FilePickerActivity.MODE_NEW_FILE;
|
||||
break;
|
||||
case R.id.radioFile:
|
||||
default:
|
||||
mode = AbstractFilePickerFragment.MODE_FILE;
|
||||
mode = FilePickerActivity.MODE_FILE;
|
||||
break;
|
||||
}
|
||||
|
||||
i.putExtra(FilePickerActivity.EXTRA_MODE, mode);
|
||||
|
||||
|
||||
// Warn about invalid combination
|
||||
if (mode == FilePickerActivity.MODE_NEW_FILE &&
|
||||
checkAllowMultiple.isChecked()) {
|
||||
Toast.makeText(NoNonsenseFilePicker.this,
|
||||
"'New file' does not support multiple items",
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
startActivityForResult(i, CODE_SD);
|
||||
}
|
||||
});
|
||||
|
|
@ -118,6 +133,8 @@ public class NoNonsenseFilePicker extends Activity {
|
|||
checkAllowMultiple.isChecked());
|
||||
i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR,
|
||||
checkAllowCreateDir.isChecked());
|
||||
i.putExtra(FilePickerActivity.EXTRA_ALLOW_EXISTING_FILE,
|
||||
checkAllowExistingFile.isChecked());
|
||||
|
||||
// What mode is selected (makes no sense to restrict to folders here)
|
||||
final int mode;
|
||||
|
|
@ -126,6 +143,9 @@ public class NoNonsenseFilePicker extends Activity {
|
|||
mode =
|
||||
AbstractFilePickerFragment.MODE_FILE_AND_DIR;
|
||||
break;
|
||||
case R.id.radioNewFile:
|
||||
mode = FilePickerActivity.MODE_NEW_FILE;
|
||||
break;
|
||||
case R.id.radioFile:
|
||||
default:
|
||||
mode = AbstractFilePickerFragment.MODE_FILE;
|
||||
|
|
@ -158,6 +178,8 @@ public class NoNonsenseFilePicker extends Activity {
|
|||
checkAllowMultiple.isChecked());
|
||||
i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR,
|
||||
checkAllowCreateDir.isChecked());
|
||||
i.putExtra(FilePickerActivity.EXTRA_ALLOW_EXISTING_FILE,
|
||||
checkAllowExistingFile.isChecked());
|
||||
|
||||
// What mode is selected (makes no sense to restrict to folders here)
|
||||
final int mode;
|
||||
|
|
@ -169,6 +191,9 @@ public class NoNonsenseFilePicker extends Activity {
|
|||
mode =
|
||||
AbstractFilePickerFragment.MODE_FILE_AND_DIR;
|
||||
break;
|
||||
case R.id.radioNewFile:
|
||||
mode = FilePickerActivity.MODE_NEW_FILE;
|
||||
break;
|
||||
case R.id.radioFile:
|
||||
default:
|
||||
mode = AbstractFilePickerFragment.MODE_FILE;
|
||||
|
|
@ -212,6 +237,8 @@ public class NoNonsenseFilePicker extends Activity {
|
|||
i.putExtra(
|
||||
FilePickerActivity.EXTRA_ALLOW_CREATE_DIR,
|
||||
checkAllowCreateDir.isChecked());
|
||||
i.putExtra(FilePickerActivity.EXTRA_ALLOW_EXISTING_FILE,
|
||||
checkAllowExistingFile.isChecked());
|
||||
|
||||
// What mode is selected
|
||||
final int mode;
|
||||
|
|
@ -223,6 +250,9 @@ public class NoNonsenseFilePicker extends Activity {
|
|||
mode =
|
||||
AbstractFilePickerFragment.MODE_FILE_AND_DIR;
|
||||
break;
|
||||
case R.id.radioNewFile:
|
||||
mode = FilePickerActivity.MODE_NEW_FILE;
|
||||
break;
|
||||
case R.id.radioFile:
|
||||
default:
|
||||
mode = AbstractFilePickerFragment.MODE_FILE;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ package com.nononsenseapps.filepicker.sample.dropbox;
|
|||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.dropbox.client2.DropboxAPI;
|
||||
import com.dropbox.client2.android.AndroidAuthSession;
|
||||
|
|
@ -34,8 +35,8 @@ public class DropboxFilePickerActivity
|
|||
|
||||
@Override
|
||||
protected AbstractFilePickerFragment<DropboxAPI.Entry> getFragment(
|
||||
@NonNull final String startPath, final int mode, final boolean allowMultiple,
|
||||
final boolean allowCreateDir) {
|
||||
@Nullable final String startPath, final int mode, final boolean allowMultiple,
|
||||
final boolean allowCreateDir, boolean allowExistingFile) {
|
||||
if (mDBApi == null || !mDBApi.getSession().isLinked()) {
|
||||
// No valid authentication
|
||||
finish();
|
||||
|
|
@ -44,7 +45,7 @@ public class DropboxFilePickerActivity
|
|||
|
||||
DropboxFilePickerFragment fragment =
|
||||
new DropboxFilePickerFragment(mDBApi);
|
||||
fragment.setArgs(startPath, mode, allowMultiple, allowCreateDir);
|
||||
fragment.setArgs(startPath, mode, allowMultiple, allowCreateDir, allowExistingFile);
|
||||
return fragment;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ package com.nononsenseapps.filepicker.sample.ftp;
|
|||
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerActivity;
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerFragment;
|
||||
|
|
@ -19,8 +20,12 @@ import org.apache.commons.net.ftp.FTPClient;
|
|||
*/
|
||||
public class FtpPickerActivity extends AbstractFilePickerActivity<FtpFile> {
|
||||
@Override
|
||||
protected AbstractFilePickerFragment<FtpFile> getFragment(@NonNull String startPath, int mode, boolean allowMultiple, boolean allowCreateDir) {
|
||||
protected AbstractFilePickerFragment<FtpFile> getFragment(@Nullable String startPath, int mode,
|
||||
boolean allowMultiple,
|
||||
boolean allowCreateDir,
|
||||
boolean allowExistingFile) {
|
||||
return FtpPickerFragment.newInstance(startPath, mode, allowMultiple, allowCreateDir,
|
||||
allowExistingFile,
|
||||
"debian.simnet.is",
|
||||
FTPClient.DEFAULT_PORT,
|
||||
null,
|
||||
|
|
|
|||
|
|
@ -53,13 +53,14 @@ public class FtpPickerFragment extends AbstractFilePickerFragment<FtpFile> {
|
|||
public static AbstractFilePickerFragment<FtpFile> newInstance(String startPath, int mode,
|
||||
boolean allowMultiple,
|
||||
boolean allowCreateDir,
|
||||
boolean allowExistingFile,
|
||||
String server, int port,
|
||||
String username,
|
||||
String password,
|
||||
String rootDir) {
|
||||
FtpPickerFragment fragment = new FtpPickerFragment();
|
||||
// Add arguments
|
||||
fragment.setArgs(startPath, mode, allowMultiple, allowCreateDir);
|
||||
fragment.setArgs(startPath, mode, allowMultiple, allowCreateDir, allowExistingFile);
|
||||
Bundle args = fragment.getArguments();
|
||||
|
||||
// Add ftp related stuff
|
||||
|
|
|
|||
|
|
@ -47,6 +47,16 @@
|
|||
android:gravity="center_vertical"
|
||||
android:text="Select file" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/radioNewFile"
|
||||
style="?android:textAppearanceMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="left"
|
||||
android:fontFamily="light"
|
||||
android:gravity="center_vertical"
|
||||
android:text="Select new file" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/radioDir"
|
||||
style="?android:textAppearanceMedium"
|
||||
|
|
@ -66,6 +76,7 @@
|
|||
android:fontFamily="light"
|
||||
android:gravity="center_vertical"
|
||||
android:text="Select any" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
<CheckBox
|
||||
|
|
@ -88,6 +99,17 @@
|
|||
android:gravity="center_vertical"
|
||||
android:text="Allow creation of directories" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkAllowExistingFile"
|
||||
style="?android:textAppearanceMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="left"
|
||||
android:fontFamily="light"
|
||||
android:gravity="center_vertical"
|
||||
android:checked="true"
|
||||
android:text="Allow selection of existing (new) file" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkLightTheme"
|
||||
style="?android:textAppearanceMedium"
|
||||
|
|
|
|||
Loading…
Reference in New Issue