Implement MtpDocumentsProvider#queryDocument.

BUG=20274999

Change-Id: Id5c81f744ea1e28d0a0d352b52db1c33fd5edcc2
This commit is contained in:
Daichi Hirono
2015-07-29 16:10:47 +09:00
parent 1c6cd1ea49
commit e5323b7493
7 changed files with 239 additions and 16 deletions

View File

@@ -19,14 +19,44 @@ package com.android.mtp;
/**
* Static utilities for ID.
*/
abstract class Identifier {
// TODO: Make the ID persistent.
static String createRootId(long deviceId, long storageId) {
return String.format("%d:%d", deviceId, storageId);
class Identifier {
int mDeviceId;
int mStorageId;
int mObjectHandle;
static Identifier createFromRootId(String rootId) {
final String[] components = rootId.split(":");
return new Identifier(
Integer.parseInt(components[0]),
Integer.parseInt(components[1]));
}
static Identifier createFromDocumentId(String documentId) {
final String[] components = documentId.split(":");
return new Identifier(
Integer.parseInt(components[0]),
Integer.parseInt(components[1]),
Integer.parseInt(components[2]));
}
Identifier(int deviceId, int storageId) {
this(deviceId, storageId, MtpDocument.DUMMY_HANDLE_FOR_ROOT);
}
Identifier(int deviceId, int storageId, int objectHandle) {
mDeviceId = deviceId;
mStorageId = storageId;
mObjectHandle = objectHandle;
}
// TODO: Make the ID persistent.
static String createDocumentId(String rootId, long objectHandle) {
return String.format("%s:%d", rootId, objectHandle);
String toRootId() {
return String.format("%d:%d", mDeviceId, mStorageId);
}
// TODO: Make the ID persistent.
String toDocumentId() {
return String.format("%d:%d:%d", mDeviceId, mStorageId, mObjectHandle);
}
}

View File

@@ -16,8 +16,99 @@
package com.android.mtp;
import android.mtp.MtpObjectInfo;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import java.util.Date;
class MtpDocument {
static final int DUMMY_HANDLE_FOR_ROOT = 0;
// TODO: Implement model class for MTP document.
private final int mObjectHandle;
private final int mFormat;
private final String mName;
private final Date mDateModified;
private final int mSize;
private final int mThumbSize;
/**
* Constructor for root document.
*/
MtpDocument(MtpRoot root) {
this(DUMMY_HANDLE_FOR_ROOT,
0x3001, // Directory.
root.mDescription,
null, // Unknown,
(int) Math.min(root.mMaxCapacity - root.mFreeSpace, Integer.MAX_VALUE),
0);
}
MtpDocument(MtpObjectInfo objectInfo) {
this(objectInfo.getObjectHandle(),
objectInfo.getFormat(),
objectInfo.getName(),
objectInfo.getDateModified() != 0 ? new Date(objectInfo.getDateModified()) : null,
objectInfo.getCompressedSize(),
objectInfo.getThumbCompressedSize());
}
MtpDocument(int objectHandle,
int format,
String name,
Date dateModified,
int size,
int thumbSize) {
this.mObjectHandle = objectHandle;
this.mFormat = format;
this.mName = name;
this.mDateModified = dateModified;
this.mSize = size;
this.mThumbSize = thumbSize;
}
String getMimeType() {
// TODO: Add complete list of mime types.
switch (mFormat) {
case 0x3001:
return DocumentsContract.Document.MIME_TYPE_DIR;
case 0x3009:
return "audio/mp3";
case 0x3801:
return "image/jpeg";
default:
return "";
}
}
Object[] getRow(Identifier rootIdentifier, String[] columnNames) {
final Object[] rows = new Object[columnNames.length];
for (int i = 0; i < columnNames.length; i++) {
if (Document.COLUMN_DOCUMENT_ID.equals(columnNames[i])) {
final Identifier identifier = new Identifier(
rootIdentifier.mDeviceId, rootIdentifier.mStorageId, mObjectHandle);
rows[i] = identifier.toDocumentId();
} else if (Document.COLUMN_DISPLAY_NAME.equals(columnNames[i])) {
rows[i] = mName;
} else if (Document.COLUMN_MIME_TYPE.equals(columnNames[i])) {
rows[i] = getMimeType();
} else if (Document.COLUMN_LAST_MODIFIED.equals(columnNames[i])) {
rows[i] = mDateModified != null ? mDateModified.getTime() : null;
} else if (Document.COLUMN_FLAGS.equals(columnNames[i])) {
int flag = 0;
if (mObjectHandle != DUMMY_HANDLE_FOR_ROOT) {
flag |= DocumentsContract.Document.FLAG_SUPPORTS_DELETE;
if (mThumbSize > 0) {
flag |= DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL;
}
}
rows[i] = flag;
} else if (Document.COLUMN_SIZE.equals(columnNames[i])) {
rows[i] = mSize;
} else {
rows[i] = null;
}
}
return rows;
}
}

View File

@@ -87,14 +87,14 @@ public class MtpDocumentsProvider extends DocumentsProvider {
// TODO: Add retry logic here.
for (final MtpRoot root : roots) {
final String rootId = Identifier.createRootId(deviceId, root.mStorageId);
final Identifier rootIdentifier = new Identifier(deviceId, root.mStorageId);
final MatrixCursor.RowBuilder builder = cursor.newRow();
builder.add(Root.COLUMN_ROOT_ID, rootId);
builder.add(Root.COLUMN_ROOT_ID, rootIdentifier.toRootId());
builder.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_IS_CHILD);
builder.add(Root.COLUMN_TITLE, root.mDescription);
builder.add(
Root.COLUMN_DOCUMENT_ID,
Identifier.createDocumentId(rootId, MtpDocument.DUMMY_HANDLE_FOR_ROOT));
rootIdentifier.toDocumentId());
builder.add(Root.COLUMN_AVAILABLE_BYTES , root.mFreeSpace);
}
} catch (IOException error) {
@@ -109,13 +109,49 @@ public class MtpDocumentsProvider extends DocumentsProvider {
@Override
public Cursor queryDocument(String documentId, String[] projection)
throws FileNotFoundException {
throw new FileNotFoundException();
if (projection == null) {
projection = MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION;
}
final Identifier identifier = Identifier.createFromDocumentId(documentId);
MtpDocument document = null;
if (identifier.mObjectHandle != MtpDocument.DUMMY_HANDLE_FOR_ROOT) {
try {
document = mMtpManager.getDocument(identifier.mDeviceId, identifier.mObjectHandle);
} catch (IOException e) {
throw new FileNotFoundException(e.getMessage());
}
} else {
MtpRoot[] roots;
try {
roots = mMtpManager.getRoots(identifier.mDeviceId);
if (roots != null) {
for (final MtpRoot root : roots) {
if (identifier.mStorageId == root.mStorageId) {
document = new MtpDocument(root);
break;
}
}
}
if (document == null) {
throw new FileNotFoundException();
}
} catch (IOException e) {
throw new FileNotFoundException(e.getMessage());
}
}
final MatrixCursor cursor = new MatrixCursor(projection);
cursor.addRow(document.getRow(
new Identifier(identifier.mDeviceId, identifier.mStorageId),
projection));
return cursor;
}
@Override
public Cursor queryChildDocuments(String parentDocumentId,
String[] projection, String sortOrder)
throws FileNotFoundException {
public Cursor queryChildDocuments(
String parentDocumentId, String[] projection, String sortOrder)
throws FileNotFoundException {
throw new FileNotFoundException();
}

View File

@@ -98,6 +98,11 @@ class MtpManager {
return results;
}
synchronized MtpDocument getDocument(int deviceId, int objectHandle) throws IOException {
final MtpDevice device = getDevice(deviceId);
return new MtpDocument(device.getObjectInfo(objectHandle));
}
private MtpDevice getDevice(int deviceId) throws IOException {
final MtpDevice device = mDevices.get(deviceId);
if (device == null) {

View File

@@ -21,14 +21,14 @@ import android.mtp.MtpStorageInfo;
import com.android.internal.annotations.VisibleForTesting;
class MtpRoot {
final long mStorageId;
final int mStorageId;
final String mDescription;
final long mFreeSpace;
final long mMaxCapacity;
final String mVolumeIdentifier;
@VisibleForTesting
MtpRoot(long storageId,
MtpRoot(int storageId,
String description,
long freeSpace,
long maxCapacity,

View File

@@ -19,12 +19,14 @@ package com.android.mtp;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Root;
import android.test.AndroidTestCase;
import android.test.mock.MockContentResolver;
import android.test.suitebuilder.annotation.SmallTest;
import java.io.IOException;
import java.util.Date;
@SmallTest
public class MtpDocumentsProviderTest extends AndroidTestCase {
@@ -167,6 +169,50 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
}
}
public void testQueryDocument() throws IOException {
mMtpManager.setDocument(0, 2, new MtpDocument(
2 /* object handle */,
0x3801 /* JPEG */,
"image.jpg" /* display name */,
new Date(1422716400000L) /* modified date */,
1024 * 1024 * 5 /* file size */,
1024 * 50 /* thumbnail size */));
final Cursor cursor = mProvider.queryDocument("0:1:2", null);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals("0:1:2", cursor.getString(0));
assertEquals("image/jpeg", cursor.getString(1));
assertEquals("image.jpg", cursor.getString(2));
assertEquals(1422716400000L, cursor.getLong(3));
assertEquals(
DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL,
cursor.getInt(4));
assertEquals(1024 * 1024 * 5, cursor.getInt(5));
}
public void testQueryDocument_forRoot() throws IOException {
mMtpManager.setRoots(0, new MtpRoot[] {
new MtpRoot(
1 /* storageId */,
"Storage A" /* volume description */,
1024 /* free space */,
4096 /* total space */,
"" /* no volume identifier */)
});
final Cursor cursor = mProvider.queryDocument("0:1:0", null);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals("0:1:0", cursor.getString(0));
assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1));
assertEquals("Storage A", cursor.getString(2));
assertTrue(cursor.isNull(3));
assertEquals(0, cursor.getInt(4));
assertEquals(3072, cursor.getInt(5));
}
private static class ContentResolver extends MockContentResolver {
int changeCount = 0;

View File

@@ -19,6 +19,7 @@ package com.android.mtp;
import android.content.Context;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -26,9 +27,14 @@ import java.util.Set;
import java.util.TreeSet;
public class TestMtpManager extends MtpManager {
private static String pack(int... args) {
return Arrays.toString(args);
}
private final Set<Integer> mValidDevices = new HashSet<Integer>();
private final Set<Integer> mOpenedDevices = new TreeSet<Integer>();
private final Map<Integer, MtpRoot[]> mRoots = new HashMap<Integer, MtpRoot[]>();
private final Map<String, MtpDocument> mDocuments = new HashMap<String, MtpDocument>();
TestMtpManager(Context context) {
super(context);
@@ -42,6 +48,10 @@ public class TestMtpManager extends MtpManager {
mRoots.put(deviceId, roots);
}
void setDocument(int deviceId, int objectHandle, MtpDocument document) {
mDocuments.put(pack(deviceId, objectHandle), document);
}
@Override
void openDevice(int deviceId) throws IOException {
if (!mValidDevices.contains(deviceId) || mOpenedDevices.contains(deviceId)) {
@@ -67,6 +77,11 @@ public class TestMtpManager extends MtpManager {
}
}
@Override
MtpDocument getDocument(int deviceId, int objectHandle) {
return mDocuments.get(pack(deviceId, objectHandle));
}
@Override
int[] getOpenedDeviceIds() {
int i = 0;