Merge "Don't show recents as copy/move destination." into nyc-dev

This commit is contained in:
Steve McKay
2016-02-18 02:53:07 +00:00
committed by Android (Google) Code Review
6 changed files with 99 additions and 59 deletions

View File

@@ -34,7 +34,6 @@ import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
@@ -74,8 +73,6 @@ public class DocumentsActivity extends BaseActivity {
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
final Resources res = getResources();
if (mState.action == ACTION_CREATE) {
final String mimeType = getIntent().getType();
final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE);

View File

@@ -66,7 +66,7 @@ public class RootsCache {
private final ContentObserver mObserver;
private OnCacheUpdateListener mCacheUpdateListener;
private final RootInfo mRecentsRoot = new RootInfo();
private final RootInfo mRecentsRoot;
private final Object mLock = new Object();
private final CountDownLatch mFirstLoad = new CountDownLatch(1);
@@ -82,6 +82,18 @@ public class RootsCache {
public RootsCache(Context context) {
mContext = context;
mObserver = new RootsChangedObserver();
// Create a new anonymous "Recents" RootInfo. It's a faker.
mRecentsRoot = new RootInfo() {{
// Special root for recents
authority = null;
rootId = null;
derivedIcon = R.drawable.ic_root_recent;
derivedType = RootInfo.TYPE_RECENTS;
flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_IS_CHILD;
title = mContext.getString(R.string.root_recent);
availableBytes = -1;
}};
}
private class RootsChangedObserver extends ContentObserver {
@@ -104,16 +116,6 @@ public class RootsCache {
* Gather roots from all known storage providers.
*/
public void updateAsync() {
// Special root for recents
mRecentsRoot.authority = null;
mRecentsRoot.rootId = null;
mRecentsRoot.derivedIcon = R.drawable.ic_root_recent;
mRecentsRoot.derivedType = RootInfo.TYPE_RECENTS;
mRecentsRoot.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE
| Root.FLAG_SUPPORTS_IS_CHILD;
mRecentsRoot.title = mContext.getString(R.string.root_recent);
mRecentsRoot.availableBytes = -1;
new UpdateTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@@ -360,7 +362,7 @@ public class RootsCache {
}
public boolean isRecentsRoot(RootInfo root) {
return mRecentsRoot == root;
return mRecentsRoot.equals(root);
}
public Collection<RootInfo> getRootsBlocking() {
@@ -400,27 +402,22 @@ public class RootsCache {
static List<RootInfo> getMatchingRoots(Collection<RootInfo> roots, State state) {
final List<RootInfo> matching = new ArrayList<>();
for (RootInfo root : roots) {
final boolean supportsCreate = (root.flags & Root.FLAG_SUPPORTS_CREATE) != 0;
final boolean supportsIsChild = (root.flags & Root.FLAG_SUPPORTS_IS_CHILD) != 0;
final boolean advanced = (root.flags & Root.FLAG_ADVANCED) != 0;
final boolean localOnly = (root.flags & Root.FLAG_LOCAL_ONLY) != 0;
final boolean empty = (root.flags & Root.FLAG_EMPTY) != 0;
// Exclude read-only devices when creating
if (state.action == State.ACTION_CREATE && !supportsCreate) continue;
if (state.action == State.ACTION_PICK_COPY_DESTINATION && !supportsCreate) continue;
if (state.action == State.ACTION_CREATE && !root.supportsCreate()) continue;
if (state.action == State.ACTION_PICK_COPY_DESTINATION
&& !root.supportsCreate()) continue;
// Exclude roots that don't support directory picking
if (state.action == State.ACTION_OPEN_TREE && !supportsIsChild) continue;
if (state.action == State.ACTION_OPEN_TREE && !root.supportsChildren()) continue;
// Exclude advanced devices when not requested
if (!state.showAdvanced && advanced) continue;
if (!state.showAdvanced && root.isAdvanced()) continue;
// Exclude non-local devices when local only
if (state.localOnly && !localOnly) continue;
if (state.localOnly && !root.isLocalOnly()) continue;
// Exclude downloads roots that don't support directory creation
// TODO: Add flag to check the root supports directory creation or not.
if (state.directoryCopy && root.isDownloads()) continue;
if (state.directoryCopy && !root.supportsChildren()) continue;
// Only show empty roots when creating, or in browse mode.
if (empty && (state.action == State.ACTION_OPEN
if (root.isEmpty() && (state.action == State.ACTION_OPEN
|| state.action == State.ACTION_GET_CONTENT)) {
if (DEBUG) Log.i(TAG, "Skipping empty root: " + root);
continue;
@@ -436,10 +433,13 @@ public class RootsCache {
// Exclude roots from the calling package.
if (state.excludedAuthorities.contains(root.authority)) {
if (DEBUG) Log.d(TAG, "Excluding root " + root.authority + " from calling package.");
if (DEBUG) Log.d(
TAG, "Excluding root " + root.authority + " from calling package.");
continue;
}
if (DEBUG) Log.d(
TAG, "Including root " + root + " in roots list.");
matching.add(root);
}
return matching;

View File

@@ -30,6 +30,7 @@ import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.Log;
@@ -86,7 +87,6 @@ public class RootsFragment extends Fragment {
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final Context context = inflater.getContext();
final View view = inflater.inflate(R.layout.fragment_roots, container, false);
mList = (ListView) view.findViewById(R.id.roots_list);
@@ -112,11 +112,13 @@ public class RootsFragment extends Fragment {
@Override
public void onLoadFinished(
Loader<Collection<RootInfo>> loader, Collection<RootInfo> result) {
if (!isAdded()) return;
if (!isAdded()) {
return;
}
final Intent includeApps = getArguments().getParcelable(EXTRA_INCLUDE_APPS);
Intent handlerAppIntent = getArguments().getParcelable(EXTRA_INCLUDE_APPS);
mAdapter = new RootsAdapter(context, result, includeApps);
mAdapter = new RootsAdapter(context, result, handlerAppIntent);
mList.setAdapter(mAdapter);
onCurrentRootChanged();
@@ -151,7 +153,9 @@ public class RootsFragment extends Fragment {
}
public void onCurrentRootChanged() {
if (mAdapter == null) return;
if (mAdapter == null) {
return;
}
final RootInfo root = ((BaseActivity) getActivity()).getCurrentRoot();
for (int i = 0; i < mAdapter.getCount(); i++) {
@@ -300,7 +304,13 @@ public class RootsFragment extends Fragment {
}
private static class RootsAdapter extends ArrayAdapter<Item> {
public RootsAdapter(Context context, Collection<RootInfo> roots, Intent includeApps) {
/**
* @param handlerAppIntent When not null, apps capable of handling the original
* intent will be included in list of roots (in special section at bottom).
*/
public RootsAdapter(
Context context, Collection<RootInfo> roots, @Nullable Intent handlerAppIntent) {
super(context, 0);
final List<RootItem> libraries = new ArrayList<>();
@@ -322,27 +332,39 @@ public class RootsFragment extends Fragment {
Collections.sort(others, comp);
addAll(libraries);
add(new SpacerItem());
// Only add the spacer if it is actually separating something.
if (!libraries.isEmpty() && !others.isEmpty()) {
add(new SpacerItem());
}
addAll(others);
if (includeApps != null) {
final PackageManager pm = context.getPackageManager();
final List<ResolveInfo> infos = pm.queryIntentActivities(
includeApps, PackageManager.MATCH_DEFAULT_ONLY);
// Include apps that can handle this intent too.
if (handlerAppIntent != null) {
includeHandlerApps(context, handlerAppIntent);
}
}
final List<AppItem> apps = new ArrayList<>();
/**
* Adds apps capable of handling the original intent will be included
* in list of roots (in special section at bottom).
*/
private void includeHandlerApps(Context context, Intent handlerAppIntent) {
final PackageManager pm = context.getPackageManager();
final List<ResolveInfo> infos = pm.queryIntentActivities(
handlerAppIntent, PackageManager.MATCH_DEFAULT_ONLY);
// Omit ourselves from the list
for (ResolveInfo info : infos) {
if (!context.getPackageName().equals(info.activityInfo.packageName)) {
apps.add(new AppItem(info));
}
final List<AppItem> apps = new ArrayList<>();
// Omit ourselves from the list
for (ResolveInfo info : infos) {
if (!context.getPackageName().equals(info.activityInfo.packageName)) {
apps.add(new AppItem(info));
}
}
if (apps.size() > 0) {
add(new SpacerItem());
addAll(apps);
}
if (apps.size() > 0) {
add(new SpacerItem());
addAll(apps);
}
}

View File

@@ -50,7 +50,7 @@ public class RootsLoader extends AsyncTaskLoader<Collection<RootInfo>> {
if (isReset()) {
return;
}
Collection<RootInfo> oldResult = mResult;
mResult = result;
if (isStarted()) {

View File

@@ -985,7 +985,8 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
}
private void copyDocuments(final List<DocumentInfo> docs, final DocumentInfo destination) {
if (!canCopy(docs, destination)) {
BaseActivity activity = (BaseActivity) getActivity();
if (!canCopy(docs, activity.getCurrentRoot(), destination)) {
Snackbars.makeSnackbar(
getActivity(),
R.string.clipboard_files_cannot_paste,
@@ -1065,13 +1066,13 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
*
* @return true if the list of files can be copied to destination.
*/
boolean canCopy(List<DocumentInfo> files, DocumentInfo dest) {
BaseActivity activity = (BaseActivity) getActivity();
private boolean canCopy(List<DocumentInfo> files, RootInfo root, DocumentInfo dest) {
if (dest == null || !dest.isDirectory() || !dest.isCreateSupported()) {
return false;
}
final RootInfo root = activity.getCurrentRoot();
// Can't copy folders to Downloads.
if (root.isDownloads()) {
// Can't copy folders to roots that don't support children.
if (!root.supportsChildren()) {
for (DocumentInfo docs : files) {
if (docs.isDirectory()) {
return false;
@@ -1079,7 +1080,7 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
}
}
return dest != null && dest.isDirectory() && dest.isCreateSupported();
return true;
}
public void selectAllFiles() {

View File

@@ -249,6 +249,26 @@ public class RootInfo implements Durable, Parcelable {
return (flags & Root.FLAG_HAS_SETTINGS) != 0;
}
public boolean supportsChildren() {
return (flags & Root.FLAG_SUPPORTS_IS_CHILD) != 0;
}
public boolean supportsCreate() {
return (flags & Root.FLAG_SUPPORTS_CREATE) != 0;
}
public boolean isAdvanced() {
return (flags & Root.FLAG_ADVANCED) != 0;
}
public boolean isLocalOnly() {
return (flags & Root.FLAG_LOCAL_ONLY) != 0;
}
public boolean isEmpty() {
return (flags & Root.FLAG_EMPTY) != 0;
}
public Drawable loadIcon(Context context) {
if (derivedIcon != 0) {
return context.getDrawable(derivedIcon);