Compare commits

..

No commits in common. "master" and "feature/purge" have entirely different histories.

8 changed files with 155 additions and 112 deletions

View File

@ -6,15 +6,18 @@ apply plugin: 'com.android.library'
*/
android {
compileSdkVersion compileSdk
compileSdkVersion 25
buildToolsVersion '25.0.2'
defaultConfig {
minSdkVersion 16
minSdkVersion 9
targetSdkVersion 25
}
}
dependencies {
implementation "com.android.support:appcompat-v7:$supportLibraryVersion"
implementation "com.android.support:support-v4:$supportLibraryVersion"
implementation "com.android.support:recyclerview-v7:$supportLibraryVersion"
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:support-v4:25.3.1'
compile 'com.android.support:recyclerview-v7:25.3.1'
}

View File

@ -14,10 +14,9 @@ import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StyleRes;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.Loader;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.util.SortedList;
@ -117,11 +116,54 @@ public abstract class AbstractFilePickerFragment<T> extends DialogFragment
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?
* @param allowExistingFile if selecting a "new" file, can existing files be chosen
* @param singleClick selecting an item does not require a press on OK
*/
public void setArgs(@Nullable final String startPath, final int mode,
final boolean allowMultiple, final boolean allowDirCreate,
final boolean allowExistingFile, final boolean singleClick) {
// 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'");
}
// Single click only makes sense if we are not selecting multiple items
if (singleClick && allowMultiple) {
throw new IllegalArgumentException("'singleClick' can not be used with '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.putBoolean(KEY_SINGLE_CLICK, singleClick);
b.putInt(KEY_MODE, mode);
setArguments(b);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
getContext().getTheme().applyStyle(getStyleId(), true);
final View view = inflater.inflate(R.layout.nnf_fragment_filepicker, container, false);
final View view = inflateRootView(inflater, container);
Toolbar toolbar = (Toolbar) view.findViewById(R.id.nnf_picker_toolbar);
if (toolbar != null) {
@ -150,11 +192,11 @@ public abstract class AbstractFilePickerFragment<T> extends DialogFragment
});
view.findViewById(R.id.nnf_button_ok).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
onClickOk(v);
}
});
@Override
public void onClick(final View v) {
onClickOk(v);
}
});
view.findViewById(R.id.nnf_button_ok_newfile).setOnClickListener(
new View.OnClickListener() {
@Override
@ -194,6 +236,10 @@ public abstract class AbstractFilePickerFragment<T> extends DialogFragment
return view;
}
protected View inflateRootView(LayoutInflater inflater, ViewGroup container) {
return inflater.inflate( R.layout.nnf_fragment_filepicker, container, false);
}
/**
* Checks if a divider drawable has been defined in the current theme. If it has, will apply
* an item decoration with the divider. If no divider has been specified, then does nothing.
@ -218,7 +264,6 @@ public abstract class AbstractFilePickerFragment<T> extends DialogFragment
public void onClickCancel(@NonNull View view) {
if (mListener != null) {
mListener.onCancelled();
dismiss();
}
}
@ -276,10 +321,10 @@ public abstract class AbstractFilePickerFragment<T> extends DialogFragment
mListener.onFilePicked(toUri(getFirstCheckedItem()));
}
}
dismiss();
}
/**
*
* @return filename as entered/picked by the user for the new file
*/
@NonNull
@ -341,15 +386,10 @@ public abstract class AbstractFilePickerFragment<T> extends DialogFragment
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@ -369,7 +409,6 @@ public abstract class AbstractFilePickerFragment<T> extends DialogFragment
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Only if we have no state
if (mCurrentPath == null) {
if (savedInstanceState != null) {
@ -412,27 +451,25 @@ public abstract class AbstractFilePickerFragment<T> extends DialogFragment
}
}
setModeView(getView());
setModeView();
// If still null
if (mCurrentPath == null) {
mCurrentPath = getRoot();
}
refresh(mCurrentPath);
}
/**
* Hides/Shows appropriate views depending on mode
*/
protected void setModeView(@NonNull View view) {
protected void setModeView() {
boolean nf = mode == MODE_NEW_FILE;
mNewFileButtonContainer.setVisibility(nf ? View.VISIBLE : View.GONE);
mRegularButtonContainer.setVisibility(nf ? View.GONE : View.VISIBLE);
if (!nf && singleClick) {
view.findViewById(R.id.nnf_button_ok).setVisibility(View.GONE);
getActivity().findViewById(R.id.nnf_button_ok).setVisibility(View.GONE);
}
}
@ -470,6 +507,12 @@ public abstract class AbstractFilePickerFragment<T> extends DialogFragment
b.putInt(KEY_MODE, mode);
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
/**
* Refreshes the list. Call this when current path changes. This method also checks
* if permissions are granted and requests them if necessary. See hasPermission()
@ -585,7 +628,6 @@ public abstract class AbstractFilePickerFragment<T> extends DialogFragment
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View v;
getContext().getTheme().applyStyle(getStyleId(), true);
switch (viewType) {
case LogicHandler.VIEWTYPE_HEADER:
v = LayoutInflater.from(getActivity()).inflate(R.layout.nnf_filepicker_listitem_dir,
@ -603,9 +645,6 @@ public abstract class AbstractFilePickerFragment<T> extends DialogFragment
}
}
@StyleRes
protected abstract int getStyleId();
/**
* @param vh to bind data from either a file or directory
* @param position 0 - n, where the header has been subtracted
@ -827,7 +866,6 @@ public abstract class AbstractFilePickerFragment<T> extends DialogFragment
v.setOnLongClickListener(this);
icon = v.findViewById(R.id.item_icon);
text = (TextView) v.findViewById(android.R.id.text1);
text.setTextColor(ContextCompat.getColor(getActivity(), android.R.color.white));
}
/**

View File

@ -0,0 +1,33 @@
/*
* 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.annotation.SuppressLint;
import android.os.Environment;
import android.support.annotation.Nullable;
import java.io.File;
@SuppressLint("Registered")
public class FilePickerActivity extends AbstractFilePickerActivity<File> {
public FilePickerActivity() {
super();
}
@Override
protected AbstractFilePickerFragment<File> getFragment(
@Nullable final String startPath, final int mode, final boolean allowMultiple,
final boolean allowCreateDir, final boolean allowExistingFile,
final boolean singleClick) {
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, allowExistingFile, singleClick);
return fragment;
}
}

View File

@ -19,27 +19,26 @@ import android.support.v7.widget.util.SortedListAdapterCallback;
import android.widget.Toast;
import java.io.File;
import java.util.Locale;
/**
* An implementation of the picker which allows you to select a file from the internal/external
* storage (SD-card) on a device.
*/
public abstract class FilePickerFragment extends AbstractFilePickerFragment<File> {
private static final String[] extensions = new String[]{".doc", ".docx", ".xlsx", ".xls", ".png", ".jpg",
".tif", ".tiff", ".pdf", ".jpeg", ".ppt", ".pptx"};
public class FilePickerFragment extends AbstractFilePickerFragment<File> {
protected static final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
protected boolean showHiddenItems = false;
private File mRequestedPath = null;
public FilePickerFragment() {
}
/**
* This method is used to dictate whether hidden files and folders should be shown or not
*
* @param showHiddenItems whether hidden items should be shown or not
*/
public void showHiddenItems(boolean showHiddenItems) {
public void showHiddenItems(boolean showHiddenItems){
this.showHiddenItems = showHiddenItems;
}
@ -49,7 +48,7 @@ public abstract class FilePickerFragment extends AbstractFilePickerFragment<File
* @return true if hidden items are shown, otherwise false
*/
public boolean areHiddenItemsShown() {
public boolean areHiddenItemsShown(){
return showHiddenItems;
}
@ -96,7 +95,6 @@ public abstract class FilePickerFragment extends AbstractFilePickerFragment<File
// Treat this as a cancel press
if (mListener != null) {
mListener.onCancelled();
dismiss();
}
} else { // if (requestCode == PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE) {
if (PackageManager.PERMISSION_GRANTED == grantResults[0]) {
@ -110,7 +108,6 @@ public abstract class FilePickerFragment extends AbstractFilePickerFragment<File
// Treat this as a cancel press
if (mListener != null) {
mListener.onCancelled();
dismiss();
}
}
}
@ -239,7 +236,7 @@ public abstract class FilePickerFragment extends AbstractFilePickerFragment<File
files.beginBatchedUpdates();
if (listFiles != null) {
for (java.io.File f : listFiles) {
if (isItemVisible(f) && directoryHasNeededFiles(f)) {
if (isItemVisible(f)) {
files.add(f);
}
}
@ -295,23 +292,6 @@ public abstract class FilePickerFragment extends AbstractFilePickerFragment<File
};
}
private boolean directoryHasNeededFiles(@NonNull final File file) {
final File[] fileList = file.listFiles();
if (fileList == null) {
return isNeededExtension(file.toString());
}
return true;
}
private boolean isNeededExtension(@NonNull final String fileName) {
for (final String extension : extensions) {
if (fileName.endsWith(extension) || fileName.endsWith(extension.toUpperCase(Locale.getDefault()))) {
return true;
}
}
return false;
}
/**
* Name is validated to be non-null, non-empty and not containing any
* slashes.
@ -368,5 +348,4 @@ public abstract class FilePickerFragment extends AbstractFilePickerFragment<File
return lhs.getName().compareToIgnoreCase(rhs.getName());
}
}
}

View File

@ -6,23 +6,22 @@
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/edit_text"
style="?android:textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="48dp"
android:fontFamily="light"
android:padding="4dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:fontFamily="light"
android:hint="@string/nnf_name"
android:maxLines="1"
android:gravity="center_vertical"
android:hint="@string/nnf_name"
android:imeOptions="actionDone"
style="?android:textAppearanceMedium"
android:inputType="textAutoComplete|textAutoCorrect"
android:padding="4dp"
tools:ignore="UnusedAttribute"/>
tools:ignore="UnusedAttribute" />
</FrameLayout>

View File

@ -5,17 +5,17 @@
~ 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/.
-->
<LinearLayout android:id="@+id/nnf_item_container"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="?android:listPreferredItemHeight"
android:background="?selectableItemBackground"
android:focusable="true"
android:minHeight="?android:listPreferredItemHeight"
android:nextFocusLeft="@+id/nnf_button_cancel"
android:nextFocusRight="@id/checkbox"
android:orientation="horizontal">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nnf_item_container"
android:layout_width="match_parent"
android:layout_height="?android:listPreferredItemHeight"
android:background="?selectableItemBackground"
android:focusable="true"
android:minHeight="?android:listPreferredItemHeight"
android:nextFocusLeft="@+id/nnf_button_cancel"
android:nextFocusRight="@id/checkbox"
android:orientation="horizontal">
<!--suppress AndroidDomInspection -->
@ -38,7 +38,6 @@
android:layout_weight="1"
android:ellipsize="end"
android:gravity="center_vertical"
android:textColor="@android:color/black"
android:maxLines="1"
android:padding="8dp"
android:text="@string/nnf_name" />
@ -51,6 +50,6 @@
android:nextFocusRight="@+id/nnf_button_ok"
android:paddingEnd="8dp"
android:paddingRight="8dp"
tools:ignore="RtlSymmetry"/>
tools:ignore="RtlSymmetry" />
</LinearLayout>

View File

@ -5,17 +5,17 @@
~ 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/.
-->
<LinearLayout android:id="@+id/nnf_item_container"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="?android:listPreferredItemHeight"
android:background="?selectableItemBackground"
android:focusable="true"
android:minHeight="?android:listPreferredItemHeight"
android:nextFocusLeft="@+id/nnf_button_cancel"
android:nextFocusRight="@+id/nnf_button_ok"
android:orientation="horizontal">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nnf_item_container"
android:layout_width="match_parent"
android:layout_height="?android:listPreferredItemHeight"
android:background="?selectableItemBackground"
android:focusable="true"
android:minHeight="?android:listPreferredItemHeight"
android:nextFocusLeft="@+id/nnf_button_cancel"
android:nextFocusRight="@+id/nnf_button_ok"
android:orientation="horizontal">
<!--suppress AndroidDomInspection -->
<ImageView
@ -27,7 +27,7 @@
android:src="@drawable/nnf_ic_folder_black_48dp"
android:tint="?attr/nnf_dir_icon_color"
android:visibility="visible"
tools:ignore="ContentDescription,VectorDrawableCompat"/>
tools:ignore="ContentDescription,VectorDrawableCompat" />
<TextView
android:id="@android:id/text1"
@ -37,8 +37,7 @@
android:layout_weight="1"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
android:padding="8dp"
android:text="@string/nnf_name"
android:textColor="@android:color/white"/>
android:text="@string/nnf_name" />
</LinearLayout>

View File

@ -7,9 +7,10 @@
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FilePickerActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/nnf_picker_toolbar"
@ -25,14 +26,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="start"
android:maxLines="1"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
tools:ignore="Deprecated"/>
<!--
suppressing deprecation of singleLine to fix crash related to ellipsize="start", see bug:
https://issuetracker.google.com/issues/36950033
-->
android:maxLines="1"/>
</android.support.v7.widget.Toolbar>
<android.support.v7.widget.RecyclerView
@ -63,8 +58,7 @@
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1"
android:text="@android:string/cancel"
android:textColor="@android:color/white"/>
android:text="@android:string/cancel"/>
<Button
android:id="@+id/nnf_button_ok"
@ -72,8 +66,7 @@
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1"
android:text="@android:string/ok"
android:textColor="@android:color/white"/>
android:text="@android:string/ok"/>
</LinearLayout>