Merge "Undo generalization isDownloads > supportsChildren." into nyc-dev
This commit is contained in:
@@ -18,6 +18,7 @@ package com.android.documentsui;
|
|||||||
|
|
||||||
import static com.android.documentsui.Shared.DEBUG;
|
import static com.android.documentsui.Shared.DEBUG;
|
||||||
import static com.android.documentsui.Shared.TAG;
|
import static com.android.documentsui.Shared.TAG;
|
||||||
|
import static com.android.internal.util.Preconditions.checkState;
|
||||||
|
|
||||||
import android.content.ContentProviderClient;
|
import android.content.ContentProviderClient;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
@@ -40,6 +41,7 @@ import android.util.Log;
|
|||||||
|
|
||||||
import com.android.documentsui.model.RootInfo;
|
import com.android.documentsui.model.RootInfo;
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
|
import com.android.internal.util.Preconditions;
|
||||||
|
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
@@ -85,15 +87,13 @@ public class RootsCache {
|
|||||||
|
|
||||||
// Create a new anonymous "Recents" RootInfo. It's a faker.
|
// Create a new anonymous "Recents" RootInfo. It's a faker.
|
||||||
mRecentsRoot = new RootInfo() {{
|
mRecentsRoot = new RootInfo() {{
|
||||||
// Special root for recents
|
// Special root for recents
|
||||||
authority = null;
|
derivedIcon = R.drawable.ic_root_recent;
|
||||||
rootId = null;
|
derivedType = RootInfo.TYPE_RECENTS;
|
||||||
derivedIcon = R.drawable.ic_root_recent;
|
flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_IS_CHILD;
|
||||||
derivedType = RootInfo.TYPE_RECENTS;
|
title = mContext.getString(R.string.root_recent);
|
||||||
flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_IS_CHILD;
|
availableBytes = -1;
|
||||||
title = mContext.getString(R.string.root_recent);
|
}};
|
||||||
availableBytes = -1;
|
|
||||||
}};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class RootsChangedObserver extends ContentObserver {
|
private class RootsChangedObserver extends ContentObserver {
|
||||||
@@ -116,6 +116,16 @@ public class RootsCache {
|
|||||||
* Gather roots from all known storage providers.
|
* Gather roots from all known storage providers.
|
||||||
*/
|
*/
|
||||||
public void updateAsync() {
|
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);
|
new UpdateTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,9 +422,10 @@ public class RootsCache {
|
|||||||
if (!state.showAdvanced && root.isAdvanced()) continue;
|
if (!state.showAdvanced && root.isAdvanced()) continue;
|
||||||
// Exclude non-local devices when local only
|
// Exclude non-local devices when local only
|
||||||
if (state.localOnly && !root.isLocalOnly()) continue;
|
if (state.localOnly && !root.isLocalOnly()) continue;
|
||||||
// Exclude downloads roots that don't support directory creation
|
// Exclude downloads roots as it doesn't support directory creation (actually
|
||||||
// TODO: Add flag to check the root supports directory creation or not.
|
// we just don't show them).
|
||||||
if (state.directoryCopy && !root.supportsChildren()) continue;
|
// 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.
|
// Only show empty roots when creating, or in browse mode.
|
||||||
if (root.isEmpty() && (state.action == State.ACTION_OPEN
|
if (root.isEmpty() && (state.action == State.ACTION_OPEN
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ import android.widget.ImageView;
|
|||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.android.documentsui.model.DocumentInfo;
|
|
||||||
import com.android.documentsui.model.RootInfo;
|
import com.android.documentsui.model.RootInfo;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -403,17 +402,7 @@ public class RootsFragment extends Fragment {
|
|||||||
public static class RootComparator implements Comparator<RootItem> {
|
public static class RootComparator implements Comparator<RootItem> {
|
||||||
@Override
|
@Override
|
||||||
public int compare(RootItem lhs, RootItem rhs) {
|
public int compare(RootItem lhs, RootItem rhs) {
|
||||||
// Sort by root type, then title, then summary.
|
return lhs.root.compareTo(rhs.root);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,14 +17,17 @@
|
|||||||
package com.android.documentsui;
|
package com.android.documentsui;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.text.format.DateUtils;
|
import android.text.format.DateUtils;
|
||||||
import android.text.format.Time;
|
import android.text.format.Time;
|
||||||
|
|
||||||
|
import java.text.Collator;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public final class Shared {
|
public final class Shared {
|
||||||
|
|
||||||
/** Intent action name to pick a copy destination. */
|
/** Intent action name to pick a copy destination. */
|
||||||
public static final String ACTION_PICK_COPY_DESTINATION =
|
public static final String ACTION_PICK_COPY_DESTINATION =
|
||||||
"com.android.documentsui.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 TAG = "Documents";
|
||||||
public static final String EXTRA_STACK = "com.android.documentsui.STACK";
|
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.
|
* Generates a formatted quantity string.
|
||||||
*/
|
*/
|
||||||
@@ -76,4 +92,26 @@ public final class Shared {
|
|||||||
? (ArrayList<T>) list
|
? (ArrayList<T>) list
|
||||||
: new 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1071,8 +1071,8 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can't copy folders to roots that don't support children.
|
// Can't copy folders to downloads, because we don't show folders there.
|
||||||
if (!root.supportsChildren()) {
|
if (!root.isDownloads()) {
|
||||||
for (DocumentInfo docs : files) {
|
for (DocumentInfo docs : files) {
|
||||||
if (docs.isDirectory()) {
|
if (docs.isDirectory()) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import android.util.Log;
|
|||||||
|
|
||||||
import com.android.documentsui.DirectoryResult;
|
import com.android.documentsui.DirectoryResult;
|
||||||
import com.android.documentsui.RootCursorWrapper;
|
import com.android.documentsui.RootCursorWrapper;
|
||||||
|
import com.android.documentsui.Shared;
|
||||||
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
|
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
|
||||||
import com.android.documentsui.model.DocumentInfo;
|
import com.android.documentsui.model.DocumentInfo;
|
||||||
|
|
||||||
@@ -170,7 +171,7 @@ public class Model {
|
|||||||
final String displayName = getCursorString(
|
final String displayName = getCursorString(
|
||||||
mCursor, Document.COLUMN_DISPLAY_NAME);
|
mCursor, Document.COLUMN_DISPLAY_NAME);
|
||||||
if (Document.MIME_TYPE_DIR.equals(mimeType)) {
|
if (Document.MIME_TYPE_DIR.equals(mimeType)) {
|
||||||
stringValues[pos] = DocumentInfo.DIR_PREFIX + displayName;
|
stringValues[pos] = Shared.DIR_PREFIX + displayName;
|
||||||
} else {
|
} else {
|
||||||
stringValues[pos] = displayName;
|
stringValues[pos] = displayName;
|
||||||
}
|
}
|
||||||
@@ -227,7 +228,7 @@ public class Model {
|
|||||||
|
|
||||||
final String lhs = pivotValue;
|
final String lhs = pivotValue;
|
||||||
final String rhs = sortKey[mid];
|
final String rhs = sortKey[mid];
|
||||||
final int compare = DocumentInfo.compareToIgnoreCaseNullable(lhs, rhs);
|
final int compare = Shared.compareToIgnoreCaseNullable(lhs, rhs);
|
||||||
|
|
||||||
if (compare < 0) {
|
if (compare < 0) {
|
||||||
right = mid;
|
right = mid;
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import android.provider.DocumentsContract;
|
|||||||
import android.provider.DocumentsContract.Document;
|
import android.provider.DocumentsContract.Document;
|
||||||
import android.provider.DocumentsProvider;
|
import android.provider.DocumentsProvider;
|
||||||
import android.support.annotation.VisibleForTesting;
|
import android.support.annotation.VisibleForTesting;
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import com.android.documentsui.DocumentsApplication;
|
import com.android.documentsui.DocumentsApplication;
|
||||||
import com.android.documentsui.RootCursorWrapper;
|
import com.android.documentsui.RootCursorWrapper;
|
||||||
@@ -38,7 +37,6 @@ import java.io.DataOutputStream;
|
|||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ProtocolException;
|
import java.net.ProtocolException;
|
||||||
import java.text.Collator;
|
|
||||||
import java.util.Objects;
|
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_INIT = 1;
|
||||||
private static final int VERSION_SPLIT_URI = 2;
|
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 authority;
|
||||||
public String documentId;
|
public String documentId;
|
||||||
public String mimeType;
|
public String mimeType;
|
||||||
@@ -320,31 +311,4 @@ public class DocumentInfo implements Durable, Parcelable {
|
|||||||
fnfe.initCause(t);
|
fnfe.initCause(t);
|
||||||
throw fnfe;
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,12 @@
|
|||||||
|
|
||||||
package com.android.documentsui.model;
|
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.getCursorInt;
|
||||||
import static com.android.documentsui.model.DocumentInfo.getCursorLong;
|
import static com.android.documentsui.model.DocumentInfo.getCursorLong;
|
||||||
import static com.android.documentsui.model.DocumentInfo.getCursorString;
|
import static com.android.documentsui.model.DocumentInfo.getCursorString;
|
||||||
|
|
||||||
|
import android.annotation.IntDef;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
@@ -36,17 +38,31 @@ import com.android.documentsui.R;
|
|||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.net.ProtocolException;
|
import java.net.ProtocolException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Representation of a {@link Root}.
|
* 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_INIT = 1;
|
||||||
private static final int VERSION_DROP_TYPE = 2;
|
private static final int VERSION_DROP_TYPE = 2;
|
||||||
|
|
||||||
// The values of these constants determine the sort order of various roots in the RootsFragment.
|
// 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_IMAGES = 1;
|
||||||
public static final int TYPE_VIDEO = 2;
|
public static final int TYPE_VIDEO = 2;
|
||||||
public static final int TYPE_AUDIO = 3;
|
public static final int TYPE_AUDIO = 3;
|
||||||
@@ -69,7 +85,7 @@ public class RootInfo implements Durable, Parcelable {
|
|||||||
/** Derived fields that aren't persisted */
|
/** Derived fields that aren't persisted */
|
||||||
public String[] derivedMimeTypes;
|
public String[] derivedMimeTypes;
|
||||||
public int derivedIcon;
|
public int derivedIcon;
|
||||||
public int derivedType;
|
public @RootType int derivedType;
|
||||||
|
|
||||||
public RootInfo() {
|
public RootInfo() {
|
||||||
reset();
|
reset();
|
||||||
@@ -328,6 +344,22 @@ public class RootInfo implements Durable, Parcelable {
|
|||||||
return Objects.hash(authority, rootId);
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Root{authority=" + authority + ", rootId=" + rootId + ", title=" + title + "}";
|
return "Root{authority=" + authority + ", rootId=" + rootId + ", title=" + title + "}";
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import android.test.suitebuilder.annotation.SmallTest;
|
|||||||
|
|
||||||
import com.android.documentsui.DirectoryResult;
|
import com.android.documentsui.DirectoryResult;
|
||||||
import com.android.documentsui.RootCursorWrapper;
|
import com.android.documentsui.RootCursorWrapper;
|
||||||
|
import com.android.documentsui.Shared;
|
||||||
import com.android.documentsui.State;
|
import com.android.documentsui.State;
|
||||||
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
|
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
|
||||||
import com.android.documentsui.model.DocumentInfo;
|
import com.android.documentsui.model.DocumentInfo;
|
||||||
@@ -190,7 +191,7 @@ public class ModelTest extends AndroidTestCase {
|
|||||||
|
|
||||||
assertEquals(ITEM_COUNT, seen.cardinality());
|
assertEquals(ITEM_COUNT, seen.cardinality());
|
||||||
for (int i = 0; i < names.size()-1; ++i) {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user