Make folders unselectable in as needed.

In some intent handling modes folders shouldn't be selectable.

Note: There are probably *other* conditions where this should be true too.

Bug: 23936872
Change-Id: I5a1f6853df265d65415cc09e95005720bf5f91d7
This commit is contained in:
Steve McKay
2015-11-09 17:38:35 +09:00
parent b9765b4c15
commit f69502ca42
2 changed files with 105 additions and 44 deletions

View File

@@ -184,7 +184,7 @@ public class DirectoryFragment extends Fragment {
private Point mThumbSize;
private DocumentsAdapter mAdapter;
private LoaderCallbacks<DirectoryResult> mCallbacks;
private FragmentTuner mFragmentTuner;
private FragmentTuner mTuner;
private DocumentClipper mClipper;
// These are lazily initialized.
private LinearLayoutManager mListLayout;
@@ -319,7 +319,7 @@ public class DirectoryFragment extends Fragment {
super.onActivityCreated(savedInstanceState);
final Context context = getActivity();
final State state = getDisplayState(DirectoryFragment.this);
final State state = getDisplayState();
final RootInfo root = getArguments().getParcelable(EXTRA_ROOT);
final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC);
@@ -381,7 +381,7 @@ public class DirectoryFragment extends Fragment {
mType = getArguments().getInt(EXTRA_TYPE);
mStateKey = buildStateKey(root, doc);
mFragmentTuner = FragmentTuner.pick(state);
mTuner = FragmentTuner.pick(state);
mClipper = new DocumentClipper(context);
if (mType == TYPE_RECENT_OPEN) {
@@ -485,7 +485,7 @@ public class DirectoryFragment extends Fragment {
return;
}
CopyService.start(getActivity(), getDisplayState(this).selectedDocumentsForCopy,
CopyService.start(getActivity(), getDisplayState().selectedDocumentsForCopy,
(DocumentStack) data.getParcelableExtra(Shared.EXTRA_STACK),
data.getIntExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_COPY));
}
@@ -524,7 +524,7 @@ public class DirectoryFragment extends Fragment {
checkNotNull(cursor, "Cursor cannot be null.");
final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
if (isDocumentEnabled(docMimeType, docFlags)) {
if (mTuner.isDocumentEnabled(docMimeType, docFlags)) {
final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor);
((BaseActivity) getActivity()).onDocumentPicked(doc, mModel);
mSelectionManager.clearSelection();
@@ -540,7 +540,7 @@ public class DirectoryFragment extends Fragment {
// Remember last scroll location
final SparseArray<Parcelable> container = new SparseArray<Parcelable>();
getView().saveHierarchyState(container);
final State state = getDisplayState(this);
final State state = getDisplayState();
state.dirState.put(mStateKey, container);
}
@@ -562,7 +562,7 @@ public class DirectoryFragment extends Fragment {
public void onUserModeChanged() {
final ContentResolver resolver = getActivity().getContentResolver();
final State state = getDisplayState(this);
final State state = getDisplayState();
final RootInfo root = getArguments().getParcelable(EXTRA_ROOT);
final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC);
@@ -591,7 +591,7 @@ public class DirectoryFragment extends Fragment {
}
private void updateDisplayState() {
final State state = getDisplayState(this);
final State state = getDisplayState();
if (mLastMode == state.derivedMode && mLastShowSize == state.showSize) return;
mLastMode = state.derivedMode;
@@ -665,13 +665,13 @@ public class DirectoryFragment extends Fragment {
@Override
public boolean onBeforeItemStateChange(int position, boolean selected) {
// Directories cannot be checked
if (selected) {
final Cursor cursor = mModel.getItem(position);
checkNotNull(cursor, "Cursor cannot be null.");
final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
return isDocumentEnabled(docMimeType, docFlags);
return mTuner.canSelectType(docMimeType)
&& mTuner.isDocumentEnabled(docMimeType, docFlags);
}
return true;
}
@@ -743,7 +743,7 @@ public class DirectoryFragment extends Fragment {
private void updateActionMenu() {
checkNotNull(mMenu);
// Delegate update logic to our owning action, since specialized logic is desired.
mFragmentTuner.updateActionMenu(mMenu, mType, mNoDeleteCount == 0);
mTuner.updateActionMenu(mMenu, mType, mNoDeleteCount == 0);
Menus.disableHiddenItems(mMenu);
}
@@ -915,7 +915,7 @@ public class DirectoryFragment extends Fragment {
new GetDocumentsTask() {
@Override
void onDocumentsReady(List<DocumentInfo> docs) {
getDisplayState(DirectoryFragment.this).selectedDocumentsForCopy = docs;
getDisplayState().selectedDocumentsForCopy = docs;
boolean directoryCopy = false;
for (DocumentInfo info : docs) {
@@ -931,8 +931,8 @@ public class DirectoryFragment extends Fragment {
}.execute(selected);
}
private static State getDisplayState(Fragment fragment) {
return ((BaseActivity) fragment.getActivity()).getDisplayState();
private State getDisplayState() {
return ((BaseActivity) getActivity()).getDisplayState();
}
// Provide a reference to the views for each data item
@@ -1027,7 +1027,7 @@ public class DirectoryFragment extends Fragment {
@Override
public DocumentHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final State state = getDisplayState(DirectoryFragment.this);
final State state = getDisplayState();
final LayoutInflater inflater = LayoutInflater.from(getContext());
View item = null;
switch (state.derivedMode) {
@@ -1070,8 +1070,7 @@ public class DirectoryFragment extends Fragment {
public void onBindViewHolder(DocumentHolder holder, int position) {
final Context context = getContext();
final State state = getDisplayState(DirectoryFragment.this);
final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC);
final State state = getDisplayState();
final RootsCache roots = DocumentsApplication.getRootsCache(context);
final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache(
context, mThumbSize);
@@ -1121,7 +1120,7 @@ public class DirectoryFragment extends Fragment {
|| MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, docMimeType);
final boolean showThumbnail = supportsThumbnail && allowThumbnail && !mSvelteRecents;
final boolean enabled = isDocumentEnabled(docMimeType, docFlags);
final boolean enabled = mTuner.isDocumentEnabled(docMimeType, docFlags);
final float iconAlpha = (state.derivedMode == MODE_LIST && !enabled) ? 0.5f : 1f;
boolean cacheHit = false;
@@ -1330,22 +1329,6 @@ public class DirectoryFragment extends Fragment {
}
}
private boolean isDocumentEnabled(String docMimeType, int docFlags) {
final State state = getDisplayState(DirectoryFragment.this);
// Directories are always enabled
if (Document.MIME_TYPE_DIR.equals(docMimeType)) {
return true;
}
// Read-only files are disabled when creating
if (state.action == ACTION_CREATE && (docFlags & Document.FLAG_SUPPORTS_WRITE) == 0) {
return false;
}
return MimePredicate.mimeMatches(state.acceptMimes, docMimeType);
}
private void copyFromClipboard() {
new AsyncTask<Void, Void, List<DocumentInfo>>() {
@@ -1393,7 +1376,7 @@ public class DirectoryFragment extends Fragment {
return;
}
final DocumentStack curStack = getDisplayState(DirectoryFragment.this).stack;
final DocumentStack curStack = getDisplayState().stack;
DocumentStack tmpStack = new DocumentStack();
if (destination != null) {
tmpStack.push(destination);
@@ -1590,7 +1573,7 @@ public class DirectoryFragment extends Fragment {
if (docs.size() == 1) {
final DocumentInfo doc = docs.get(0);
return getDocumentIcon(getActivity(), doc.authority, doc.documentId,
doc.mimeType, doc.icon, getDisplayState(this));
doc.mimeType, doc.icon, getDisplayState());
}
return getActivity().getDrawable(R.drawable.ic_doc_generic);
}

View File

@@ -17,39 +17,104 @@
package com.android.documentsui.dirlist;
import static com.android.documentsui.State.ACTION_BROWSE;
import static com.android.documentsui.State.ACTION_CREATE;
import static com.android.documentsui.State.ACTION_GET_CONTENT;
import static com.android.documentsui.State.ACTION_MANAGE;
import static com.android.documentsui.State.ACTION_OPEN;
import static com.android.documentsui.State.ACTION_OPEN_TREE;
import static com.android.internal.util.Preconditions.checkArgument;
import android.os.SystemProperties;
import android.view.Menu;
import android.view.MenuItem;
import com.android.documentsui.Menus;
import com.android.documentsui.MimePredicate;
import com.android.documentsui.R;
import com.android.documentsui.State;
import android.os.SystemProperties;
import android.provider.DocumentsContract.Document;
import android.view.Menu;
import android.view.MenuItem;
/**
* Providers support for specializing the DirectoryFragment to the "host" Activity.
* Feel free to expand the role of this class to handle other specializations.
*/
public abstract class FragmentTuner {
final State mState;
public FragmentTuner(State state) {
mState = state;
}
public static FragmentTuner pick(State state) {
switch (state.action) {
case ACTION_BROWSE:
return new FilesTuner();
return new FilesTuner(state);
case ACTION_MANAGE:
return new ManageTuner();
return new DownloadsTuner(state);
default:
return new DocumentsTuner();
return new DocumentsTuner(state);
}
}
public abstract void updateActionMenu(Menu menu, int dirType, boolean canDelete);
// Subtly different from isDocumentEnabled. The reason may be illuminated as follows.
// A folder is enabled such that it may be double clicked, even in settings
// when the folder itself cannot be selected. This may also be true of container types.
public boolean canSelectType(String docMimeType) {
return true;
}
public boolean isDocumentEnabled(String docMimeType, int docFlags) {
if (isDirectory(docMimeType)) {
return true;
}
return MimePredicate.mimeMatches(mState.acceptMimes, docMimeType);
}
/**
* Provides support for Platform specific specializations of DirectoryFragment.
*/
private static final class DocumentsTuner extends FragmentTuner {
public DocumentsTuner(State state) {
super(state);
}
@Override
public boolean canSelectType(String docMimeType) {
switch (mState.action) {
case ACTION_OPEN:
case ACTION_CREATE:
case ACTION_GET_CONTENT:
return !isDirectory(docMimeType);
case ACTION_OPEN_TREE:
// In this case nothing *ever* is selectable...the expected user behavior is
// they navigate *into* a folder, then click a confirmation button indicating
// that the current directory is the directory they are picking.
return false;
}
return true;
}
@Override
public boolean isDocumentEnabled(String docMimeType, int docFlags) {
// Directories are always enabled
if (isDirectory(docMimeType)) {
return true;
}
// Read-only files are disabled when creating
if (mState.action == ACTION_CREATE && (docFlags & Document.FLAG_SUPPORTS_WRITE) == 0) {
return false;
}
return MimePredicate.mimeMatches(mState.acceptMimes, docMimeType);
}
@Override
public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
@@ -77,7 +142,11 @@ public abstract class FragmentTuner {
/**
* Provides support for Platform specific specializations of DirectoryFragment.
*/
private static final class ManageTuner extends FragmentTuner {
private static final class DownloadsTuner extends FragmentTuner {
public DownloadsTuner(State state) {
super(state);
}
@Override
public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
@@ -107,6 +176,11 @@ public abstract class FragmentTuner {
* Provides support for Files activity specific specializations of DirectoryFragment.
*/
private static final class FilesTuner extends FragmentTuner {
public FilesTuner(State state) {
super(state);
}
@Override
public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
@@ -124,4 +198,8 @@ public abstract class FragmentTuner {
Menus.disableHiddenItems(menu, copy, paste);
}
}
private static boolean isDirectory(String mimeType) {
return Document.MIME_TYPE_DIR.equals(mimeType);
}
}