Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocument.java +22 −34 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.mtp; import android.database.MatrixCursor; import android.mtp.MtpObjectInfo; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; Loading Loading @@ -67,11 +68,29 @@ class MtpDocument { this.mThumbSize = thumbSize; } int getSize() { return mSize; void addToCursor(Identifier rootIdentifier, MatrixCursor.RowBuilder builder) { final Identifier identifier = new Identifier( rootIdentifier.mDeviceId, rootIdentifier.mStorageId, mObjectHandle); int flag = 0; if (mObjectHandle != DUMMY_HANDLE_FOR_ROOT) { flag |= DocumentsContract.Document.FLAG_SUPPORTS_DELETE; if (mThumbSize > 0) { flag |= DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL; } } String getMimeType() { builder.add(Document.COLUMN_DOCUMENT_ID, identifier.toDocumentId()); builder.add(Document.COLUMN_DISPLAY_NAME, mName); builder.add(Document.COLUMN_MIME_TYPE, getMimeType()); builder.add( Document.COLUMN_LAST_MODIFIED, mDateModified != null ? mDateModified.getTime() : null); builder.add(Document.COLUMN_FLAGS, flag); builder.add(Document.COLUMN_SIZE, mSize); } private String getMimeType() { // TODO: Add complete list of mime types. switch (mFormat) { case 0x3001: Loading @@ -84,35 +103,4 @@ class MtpDocument { 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; } } packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +35 −9 Original line number Diff line number Diff line Loading @@ -147,17 +147,45 @@ public class MtpDocumentsProvider extends DocumentsProvider { } final MatrixCursor cursor = new MatrixCursor(projection); cursor.addRow(document.getRow( new Identifier(identifier.mDeviceId, identifier.mStorageId), projection)); document.addToCursor( new Identifier(identifier.mDeviceId, identifier.mStorageId), cursor.newRow()); return cursor; } // TODO: Support background loading for large number of files. @Override public Cursor queryChildDocuments( String parentDocumentId, String[] projection, String sortOrder) throws FileNotFoundException { throw new FileNotFoundException(); public Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder) throws FileNotFoundException { if (projection == null) { projection = MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION; } final Identifier parentIdentifier = Identifier.createFromDocumentId(parentDocumentId); int parentHandle = parentIdentifier.mObjectHandle; // Need to pass the special value MtpManager.OBJECT_HANDLE_ROOT_CHILDREN to // getObjectHandles if we would like to obtain children under the root. if (parentHandle == MtpDocument.DUMMY_HANDLE_FOR_ROOT) { parentHandle = MtpManager.OBJECT_HANDLE_ROOT_CHILDREN; } try { final MatrixCursor cursor = new MatrixCursor(projection); final Identifier rootIdentifier = new Identifier( parentIdentifier.mDeviceId, parentIdentifier.mStorageId); final int[] objectHandles = mMtpManager.getObjectHandles( parentIdentifier.mDeviceId, parentIdentifier.mStorageId, parentHandle); for (int i = 0; i < objectHandles.length; i++) { try { final MtpDocument document = mMtpManager.getDocument( parentIdentifier.mDeviceId, objectHandles[i]); document.addToCursor(rootIdentifier, cursor.newRow()); } catch (IOException error) { cursor.close(); throw new FileNotFoundException(error.getMessage()); } } return cursor; } catch (IOException exception) { throw new FileNotFoundException(exception.getMessage()); } } @Override Loading @@ -170,8 +198,6 @@ public class MtpDocumentsProvider extends DocumentsProvider { } final Identifier identifier = Identifier.createFromDocumentId(documentId); try { final MtpDocument document = mMtpManager.getDocument(identifier.mDeviceId, identifier.mObjectHandle); return mPipeManager.readDocument(mMtpManager, identifier); } catch (IOException error) { throw new FileNotFoundException(error.getMessage()); Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java +8 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ import java.io.IOException; * The model wrapping android.mtp API. */ class MtpManager { final static int OBJECT_HANDLE_ROOT_CHILDREN = -1; private final UsbManager mManager; // TODO: Save and restore the set of opened device. private final SparseArray<MtpDevice> mDevices = new SparseArray<MtpDevice>(); Loading Loading @@ -105,6 +107,12 @@ class MtpManager { return new MtpDocument(device.getObjectInfo(objectHandle)); } synchronized int[] getObjectHandles(int deviceId, int storageId, int parentObjectHandle) throws IOException { final MtpDevice device = getDevice(deviceId); return device.getObjectHandles(storageId, 0 /* all format */, parentObjectHandle); } synchronized byte[] getObject(int deviceId, int objectHandle, int expectedSize) throws IOException { final MtpDevice device = getDevice(deviceId); Loading packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java +48 −1 Original line number Diff line number Diff line Loading @@ -217,6 +217,53 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { assertEquals(3072, cursor.getInt(5)); } public void testQueryChildDocuments() throws Exception { mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 }); mMtpManager.setDocument(0, 1, new MtpDocument( 1 /* object handle */, 0x3801 /* JPEG */, "image.jpg" /* display name */, new Date(0) /* modified date */, 1024 * 1024 * 5 /* file size */, 1024 * 50 /* thumbnail size */)); final Cursor cursor = mProvider.queryChildDocuments("0_0_0", null, null); assertEquals(1, cursor.getCount()); assertTrue(cursor.moveToNext()); assertEquals("0_0_1", cursor.getString(0)); assertEquals("image/jpeg", cursor.getString(1)); assertEquals("image.jpg", cursor.getString(2)); assertEquals(0, cursor.getLong(3)); assertEquals( DocumentsContract.Document.FLAG_SUPPORTS_DELETE | DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL, cursor.getInt(4)); assertEquals(1024 * 1024 * 5, cursor.getInt(5)); assertFalse(cursor.moveToNext()); } public void testQueryChildDocuments_cursorError() throws Exception { try { mProvider.queryChildDocuments("0_0_0", null, null); fail(); } catch (Throwable error) { assertTrue(error instanceof FileNotFoundException); } } public void testQueryChildDocuments_documentError() throws Exception { mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 }); try { mProvider.queryChildDocuments("0_0_0", null, null); fail(); } catch (Throwable error) { assertTrue(error instanceof FileNotFoundException); } } public void testDeleteDocument() throws FileNotFoundException { mMtpManager.setDocument(0, 1, new MtpDocument( 1 /* object handle */, Loading @@ -232,7 +279,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { MtpDocumentsProvider.AUTHORITY, "0_0_2"))); } public void testDeleteDocument_error() throws FileNotFoundException { public void testDeleteDocument_error() { mMtpManager.setParent(0, 1, 2); try { mProvider.deleteDocument("0_0_1"); Loading packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java +29 −9 Original line number Diff line number Diff line Loading @@ -32,13 +32,14 @@ public class TestMtpManager extends MtpManager { 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>(); private final Map<String, byte[]> mThumbnailBytes = new HashMap<String, byte[]>(); private final Map<String, Integer> mParents = new HashMap<String, Integer>(); private final Map<String, byte[]> mImportFileBytes = new HashMap<String, byte[]>(); private final Set<Integer> mValidDevices = new HashSet<>(); private final Set<Integer> mOpenedDevices = new TreeSet<>(); private final Map<Integer, MtpRoot[]> mRoots = new HashMap<>(); private final Map<String, MtpDocument> mDocuments = new HashMap<>(); private final Map<String, int[]> mObjectHandles = new HashMap<>(); private final Map<String, byte[]> mThumbnailBytes = new HashMap<>(); private final Map<String, Integer> mParents = new HashMap<>(); private final Map<String, byte[]> mImportFileBytes = new HashMap<>(); TestMtpManager(Context context) { super(context); Loading @@ -48,6 +49,10 @@ public class TestMtpManager extends MtpManager { mValidDevices.add(deviceId); } void setObjectHandles(int deviceId, int storageId, int objectHandle, int[] documents) { mObjectHandles.put(pack(deviceId, storageId, objectHandle), documents); } void setRoots(int deviceId, MtpRoot[] roots) { mRoots.put(deviceId, roots); } Loading Loading @@ -94,8 +99,23 @@ public class TestMtpManager extends MtpManager { } @Override MtpDocument getDocument(int deviceId, int objectHandle) { return mDocuments.get(pack(deviceId, objectHandle)); MtpDocument getDocument(int deviceId, int objectHandle) throws IOException { final String key = pack(deviceId, objectHandle); if (mDocuments.containsKey(key)) { return mDocuments.get(key); } else { throw new IOException("getDocument error: " + key); } } @Override int[] getObjectHandles(int deviceId, int storageId, int parentObjectHandle) throws IOException { final String key = pack(deviceId, storageId, parentObjectHandle); if (mObjectHandles.containsKey(key)) { return mObjectHandles.get(key); } else { throw new IOException("getObjectHandles error: " + key); } } @Override Loading Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocument.java +22 −34 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.mtp; import android.database.MatrixCursor; import android.mtp.MtpObjectInfo; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; Loading Loading @@ -67,11 +68,29 @@ class MtpDocument { this.mThumbSize = thumbSize; } int getSize() { return mSize; void addToCursor(Identifier rootIdentifier, MatrixCursor.RowBuilder builder) { final Identifier identifier = new Identifier( rootIdentifier.mDeviceId, rootIdentifier.mStorageId, mObjectHandle); int flag = 0; if (mObjectHandle != DUMMY_HANDLE_FOR_ROOT) { flag |= DocumentsContract.Document.FLAG_SUPPORTS_DELETE; if (mThumbSize > 0) { flag |= DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL; } } String getMimeType() { builder.add(Document.COLUMN_DOCUMENT_ID, identifier.toDocumentId()); builder.add(Document.COLUMN_DISPLAY_NAME, mName); builder.add(Document.COLUMN_MIME_TYPE, getMimeType()); builder.add( Document.COLUMN_LAST_MODIFIED, mDateModified != null ? mDateModified.getTime() : null); builder.add(Document.COLUMN_FLAGS, flag); builder.add(Document.COLUMN_SIZE, mSize); } private String getMimeType() { // TODO: Add complete list of mime types. switch (mFormat) { case 0x3001: Loading @@ -84,35 +103,4 @@ class MtpDocument { 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; } }
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +35 −9 Original line number Diff line number Diff line Loading @@ -147,17 +147,45 @@ public class MtpDocumentsProvider extends DocumentsProvider { } final MatrixCursor cursor = new MatrixCursor(projection); cursor.addRow(document.getRow( new Identifier(identifier.mDeviceId, identifier.mStorageId), projection)); document.addToCursor( new Identifier(identifier.mDeviceId, identifier.mStorageId), cursor.newRow()); return cursor; } // TODO: Support background loading for large number of files. @Override public Cursor queryChildDocuments( String parentDocumentId, String[] projection, String sortOrder) throws FileNotFoundException { throw new FileNotFoundException(); public Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder) throws FileNotFoundException { if (projection == null) { projection = MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION; } final Identifier parentIdentifier = Identifier.createFromDocumentId(parentDocumentId); int parentHandle = parentIdentifier.mObjectHandle; // Need to pass the special value MtpManager.OBJECT_HANDLE_ROOT_CHILDREN to // getObjectHandles if we would like to obtain children under the root. if (parentHandle == MtpDocument.DUMMY_HANDLE_FOR_ROOT) { parentHandle = MtpManager.OBJECT_HANDLE_ROOT_CHILDREN; } try { final MatrixCursor cursor = new MatrixCursor(projection); final Identifier rootIdentifier = new Identifier( parentIdentifier.mDeviceId, parentIdentifier.mStorageId); final int[] objectHandles = mMtpManager.getObjectHandles( parentIdentifier.mDeviceId, parentIdentifier.mStorageId, parentHandle); for (int i = 0; i < objectHandles.length; i++) { try { final MtpDocument document = mMtpManager.getDocument( parentIdentifier.mDeviceId, objectHandles[i]); document.addToCursor(rootIdentifier, cursor.newRow()); } catch (IOException error) { cursor.close(); throw new FileNotFoundException(error.getMessage()); } } return cursor; } catch (IOException exception) { throw new FileNotFoundException(exception.getMessage()); } } @Override Loading @@ -170,8 +198,6 @@ public class MtpDocumentsProvider extends DocumentsProvider { } final Identifier identifier = Identifier.createFromDocumentId(documentId); try { final MtpDocument document = mMtpManager.getDocument(identifier.mDeviceId, identifier.mObjectHandle); return mPipeManager.readDocument(mMtpManager, identifier); } catch (IOException error) { throw new FileNotFoundException(error.getMessage()); Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java +8 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ import java.io.IOException; * The model wrapping android.mtp API. */ class MtpManager { final static int OBJECT_HANDLE_ROOT_CHILDREN = -1; private final UsbManager mManager; // TODO: Save and restore the set of opened device. private final SparseArray<MtpDevice> mDevices = new SparseArray<MtpDevice>(); Loading Loading @@ -105,6 +107,12 @@ class MtpManager { return new MtpDocument(device.getObjectInfo(objectHandle)); } synchronized int[] getObjectHandles(int deviceId, int storageId, int parentObjectHandle) throws IOException { final MtpDevice device = getDevice(deviceId); return device.getObjectHandles(storageId, 0 /* all format */, parentObjectHandle); } synchronized byte[] getObject(int deviceId, int objectHandle, int expectedSize) throws IOException { final MtpDevice device = getDevice(deviceId); Loading
packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java +48 −1 Original line number Diff line number Diff line Loading @@ -217,6 +217,53 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { assertEquals(3072, cursor.getInt(5)); } public void testQueryChildDocuments() throws Exception { mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 }); mMtpManager.setDocument(0, 1, new MtpDocument( 1 /* object handle */, 0x3801 /* JPEG */, "image.jpg" /* display name */, new Date(0) /* modified date */, 1024 * 1024 * 5 /* file size */, 1024 * 50 /* thumbnail size */)); final Cursor cursor = mProvider.queryChildDocuments("0_0_0", null, null); assertEquals(1, cursor.getCount()); assertTrue(cursor.moveToNext()); assertEquals("0_0_1", cursor.getString(0)); assertEquals("image/jpeg", cursor.getString(1)); assertEquals("image.jpg", cursor.getString(2)); assertEquals(0, cursor.getLong(3)); assertEquals( DocumentsContract.Document.FLAG_SUPPORTS_DELETE | DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL, cursor.getInt(4)); assertEquals(1024 * 1024 * 5, cursor.getInt(5)); assertFalse(cursor.moveToNext()); } public void testQueryChildDocuments_cursorError() throws Exception { try { mProvider.queryChildDocuments("0_0_0", null, null); fail(); } catch (Throwable error) { assertTrue(error instanceof FileNotFoundException); } } public void testQueryChildDocuments_documentError() throws Exception { mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 }); try { mProvider.queryChildDocuments("0_0_0", null, null); fail(); } catch (Throwable error) { assertTrue(error instanceof FileNotFoundException); } } public void testDeleteDocument() throws FileNotFoundException { mMtpManager.setDocument(0, 1, new MtpDocument( 1 /* object handle */, Loading @@ -232,7 +279,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { MtpDocumentsProvider.AUTHORITY, "0_0_2"))); } public void testDeleteDocument_error() throws FileNotFoundException { public void testDeleteDocument_error() { mMtpManager.setParent(0, 1, 2); try { mProvider.deleteDocument("0_0_1"); Loading
packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java +29 −9 Original line number Diff line number Diff line Loading @@ -32,13 +32,14 @@ public class TestMtpManager extends MtpManager { 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>(); private final Map<String, byte[]> mThumbnailBytes = new HashMap<String, byte[]>(); private final Map<String, Integer> mParents = new HashMap<String, Integer>(); private final Map<String, byte[]> mImportFileBytes = new HashMap<String, byte[]>(); private final Set<Integer> mValidDevices = new HashSet<>(); private final Set<Integer> mOpenedDevices = new TreeSet<>(); private final Map<Integer, MtpRoot[]> mRoots = new HashMap<>(); private final Map<String, MtpDocument> mDocuments = new HashMap<>(); private final Map<String, int[]> mObjectHandles = new HashMap<>(); private final Map<String, byte[]> mThumbnailBytes = new HashMap<>(); private final Map<String, Integer> mParents = new HashMap<>(); private final Map<String, byte[]> mImportFileBytes = new HashMap<>(); TestMtpManager(Context context) { super(context); Loading @@ -48,6 +49,10 @@ public class TestMtpManager extends MtpManager { mValidDevices.add(deviceId); } void setObjectHandles(int deviceId, int storageId, int objectHandle, int[] documents) { mObjectHandles.put(pack(deviceId, storageId, objectHandle), documents); } void setRoots(int deviceId, MtpRoot[] roots) { mRoots.put(deviceId, roots); } Loading Loading @@ -94,8 +99,23 @@ public class TestMtpManager extends MtpManager { } @Override MtpDocument getDocument(int deviceId, int objectHandle) { return mDocuments.get(pack(deviceId, objectHandle)); MtpDocument getDocument(int deviceId, int objectHandle) throws IOException { final String key = pack(deviceId, objectHandle); if (mDocuments.containsKey(key)) { return mDocuments.get(key); } else { throw new IOException("getDocument error: " + key); } } @Override int[] getObjectHandles(int deviceId, int storageId, int parentObjectHandle) throws IOException { final String key = pack(deviceId, storageId, parentObjectHandle); if (mObjectHandles.containsKey(key)) { return mObjectHandles.get(key); } else { throw new IOException("getObjectHandles error: " + key); } } @Override Loading