Merge "Undo generalization isDownloads > supportsChildren." into nyc-dev

This commit is contained in:
Steve McKay
2016-02-19 19:17:58 +00:00
committed by Android (Google) Code Review
8 changed files with 103 additions and 67 deletions

View File

@@ -18,6 +18,7 @@ package com.android.documentsui;
import static com.android.documentsui.Shared.DEBUG;
import static com.android.documentsui.Shared.TAG;
import static com.android.internal.util.Preconditions.checkState;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
@@ -40,6 +41,7 @@ import android.util.Log;
import com.android.documentsui.model.RootInfo;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
@@ -85,15 +87,13 @@ public class RootsCache {
// 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;
}};
// Special root for recents
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 {
@@ -116,6 +116,16 @@ public class RootsCache {
* Gather roots from all known storage providers.
*/
public void updateAsync() {
// Verifying an assumption about the recents root being immutable.
if (DEBUG) {
checkState(mRecentsRoot.authority == null);
checkState(mRecentsRoot.rootId == null);
checkState(mRecentsRoot.derivedIcon == R.drawable.ic_root_recent);
checkState(mRecentsRoot.derivedType == RootInfo.TYPE_RECENTS);
checkState(mRecentsRoot.flags == (Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_IS_CHILD));
checkState(mRecentsRoot.title == mContext.getString(R.string.root_recent));
checkState(mRecentsRoot.availableBytes == -1);
}
new UpdateTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@@ -412,9 +422,10 @@ public class RootsCache {
if (!state.showAdvanced && root.isAdvanced()) continue;
// Exclude non-local devices when local only
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.supportsChildren()) continue;
// Exclude downloads roots as it doesn't support directory creation (actually
// we just don't show them).
// TODO: Add flag to check the root supports directory creation.
if (state.directoryCopy && !root.isDownloads()) continue;
// Only show empty roots when creating, or in browse mode.
if (root.isEmpty() && (state.action == State.ACTION_OPEN

View File

@@ -45,7 +45,6 @@ import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.RootInfo;
import java.util.ArrayList;
@@ -403,17 +402,7 @@ public class RootsFragment extends Fragment {
public static class RootComparator implements Comparator<RootItem> {
@Override
public int compare(RootItem lhs, RootItem rhs) {
// Sort by root type, then title, then summary.
int score = lhs.root.derivedType - rhs.root.derivedType;
if (score != 0) {
return score;
}
score = DocumentInfo.compareToIgnoreCaseNullable(lhs.root.title, rhs.root.title);
if (score != 0) {
return score;
}
return DocumentInfo.compareToIgnoreCaseNullable(lhs.root.summary, rhs.root.summary);
return lhs.root.compareTo(rhs.root);
}
}
}

View File

@@ -17,14 +17,17 @@
package com.android.documentsui;
import android.content.Context;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.format.Time;
import java.text.Collator;
import java.util.ArrayList;
import java.util.List;
/** @hide */
public final class Shared {
/** Intent action name to pick a copy destination. */
public static final String ACTION_PICK_COPY_DESTINATION =
"com.android.documentsui.PICK_COPY_DESTINATION";
@@ -39,6 +42,19 @@ public final class Shared {
public static final String TAG = "Documents";
public static final String EXTRA_STACK = "com.android.documentsui.STACK";
/**
* String prefix used to indicate the document is a directory.
*/
public static final char DIR_PREFIX = '\001';
private static final Collator sCollator;
static {
sCollator = Collator.getInstance();
sCollator.setStrength(Collator.SECONDARY);
}
/**
* Generates a formatted quantity string.
*/
@@ -76,4 +92,26 @@ public final class Shared {
? (ArrayList<T>) list
: new ArrayList<T>(list);
}
/**
* Compare two strings against each other using system default collator in a
* case-insensitive mode. Clusters strings prefixed with {@link DIR_PREFIX}
* before other items.
*/
public static int compareToIgnoreCaseNullable(String lhs, String rhs) {
final boolean leftEmpty = TextUtils.isEmpty(lhs);
final boolean rightEmpty = TextUtils.isEmpty(rhs);
if (leftEmpty && rightEmpty) return 0;
if (leftEmpty) return -1;
if (rightEmpty) return 1;
final boolean leftDir = (lhs.charAt(0) == DIR_PREFIX);
final boolean rightDir = (rhs.charAt(0) == DIR_PREFIX);
if (leftDir && !rightDir) return -1;
if (rightDir && !leftDir) return 1;
return sCollator.compare(lhs, rhs);
}
}

View File

@@ -1071,8 +1071,8 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
return false;
}
// Can't copy folders to roots that don't support children.
if (!root.supportsChildren()) {
// Can't copy folders to downloads, because we don't show folders there.
if (!root.isDownloads()) {
for (DocumentInfo docs : files) {
if (docs.isDirectory()) {
return false;

View File

@@ -35,6 +35,7 @@ import android.util.Log;
import com.android.documentsui.DirectoryResult;
import com.android.documentsui.RootCursorWrapper;
import com.android.documentsui.Shared;
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
import com.android.documentsui.model.DocumentInfo;
@@ -170,7 +171,7 @@ public class Model {
final String displayName = getCursorString(
mCursor, Document.COLUMN_DISPLAY_NAME);
if (Document.MIME_TYPE_DIR.equals(mimeType)) {
stringValues[pos] = DocumentInfo.DIR_PREFIX + displayName;
stringValues[pos] = Shared.DIR_PREFIX + displayName;
} else {
stringValues[pos] = displayName;
}
@@ -227,7 +228,7 @@ public class Model {
final String lhs = pivotValue;
final String rhs = sortKey[mid];
final int compare = DocumentInfo.compareToIgnoreCaseNullable(lhs, rhs);
final int compare = Shared.compareToIgnoreCaseNullable(lhs, rhs);
if (compare < 0) {
right = mid;

View File

@@ -26,7 +26,6 @@ import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsProvider;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import com.android.documentsui.DocumentsApplication;
import com.android.documentsui.RootCursorWrapper;
@@ -38,7 +37,6 @@ import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.ProtocolException;
import java.text.Collator;
import java.util.Objects;
/**
@@ -48,13 +46,6 @@ public class DocumentInfo implements Durable, Parcelable {
private static final int VERSION_INIT = 1;
private static final int VERSION_SPLIT_URI = 2;
private static final Collator sCollator;
static {
sCollator = Collator.getInstance();
sCollator.setStrength(Collator.SECONDARY);
}
public String authority;
public String documentId;
public String mimeType;
@@ -320,31 +311,4 @@ public class DocumentInfo implements Durable, Parcelable {
fnfe.initCause(t);
throw fnfe;
}
/**
* String prefix used to indicate the document is a directory.
*/
public static final char DIR_PREFIX = '\001';
/**
* Compare two strings against each other using system default collator in a
* case-insensitive mode. Clusters strings prefixed with {@link #DIR_PREFIX}
* before other items.
*/
public static int compareToIgnoreCaseNullable(String lhs, String rhs) {
final boolean leftEmpty = TextUtils.isEmpty(lhs);
final boolean rightEmpty = TextUtils.isEmpty(rhs);
if (leftEmpty && rightEmpty) return 0;
if (leftEmpty) return -1;
if (rightEmpty) return 1;
final boolean leftDir = (lhs.charAt(0) == DIR_PREFIX);
final boolean rightDir = (rhs.charAt(0) == DIR_PREFIX);
if (leftDir && !rightDir) return -1;
if (rightDir && !leftDir) return 1;
return sCollator.compare(lhs, rhs);
}
}

View File

@@ -16,10 +16,12 @@
package com.android.documentsui.model;
import static com.android.documentsui.Shared.compareToIgnoreCaseNullable;
import static com.android.documentsui.model.DocumentInfo.getCursorInt;
import static com.android.documentsui.model.DocumentInfo.getCursorLong;
import static com.android.documentsui.model.DocumentInfo.getCursorString;
import android.annotation.IntDef;
import android.content.Context;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
@@ -36,17 +38,31 @@ import com.android.documentsui.R;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.ProtocolException;
import java.util.Objects;
/**
* Representation of a {@link Root}.
*/
public class RootInfo implements Durable, Parcelable {
public class RootInfo implements Durable, Parcelable, Comparable<RootInfo> {
private static final int VERSION_INIT = 1;
private static final int VERSION_DROP_TYPE = 2;
// The values of these constants determine the sort order of various roots in the RootsFragment.
@IntDef(flag = true, value = {
TYPE_IMAGES,
TYPE_VIDEO,
TYPE_AUDIO,
TYPE_RECENTS,
TYPE_DOWNLOADS,
TYPE_LOCAL,
TYPE_MTP,
TYPE_OTHER
})
@Retention(RetentionPolicy.SOURCE)
public @interface RootType {}
public static final int TYPE_IMAGES = 1;
public static final int TYPE_VIDEO = 2;
public static final int TYPE_AUDIO = 3;
@@ -69,7 +85,7 @@ public class RootInfo implements Durable, Parcelable {
/** Derived fields that aren't persisted */
public String[] derivedMimeTypes;
public int derivedIcon;
public int derivedType;
public @RootType int derivedType;
public RootInfo() {
reset();
@@ -328,6 +344,22 @@ public class RootInfo implements Durable, Parcelable {
return Objects.hash(authority, rootId);
}
@Override
public int compareTo(RootInfo other) {
// Sort by root type, then title, then summary.
int score = derivedType - other.derivedType;
if (score != 0) {
return score;
}
score = compareToIgnoreCaseNullable(title, other.title);
if (score != 0) {
return score;
}
return compareToIgnoreCaseNullable(summary, other.summary);
}
@Override
public String toString() {
return "Root{authority=" + authority + ", rootId=" + rootId + ", title=" + title + "}";

View File

@@ -28,6 +28,7 @@ import android.test.suitebuilder.annotation.SmallTest;
import com.android.documentsui.DirectoryResult;
import com.android.documentsui.RootCursorWrapper;
import com.android.documentsui.Shared;
import com.android.documentsui.State;
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
import com.android.documentsui.model.DocumentInfo;
@@ -190,7 +191,7 @@ public class ModelTest extends AndroidTestCase {
assertEquals(ITEM_COUNT, seen.cardinality());
for (int i = 0; i < names.size()-1; ++i) {
assertTrue(DocumentInfo.compareToIgnoreCaseNullable(names.get(i), names.get(i+1)) <= 0);
assertTrue(Shared.compareToIgnoreCaseNullable(names.get(i), names.get(i+1)) <= 0);
}
}