Merge changes I99b3452f,Idda56860

* changes:
  CameraBrowser: Display thumbnails for camera images.
  MTP: Add support for retrieving thumbnails to MTP content provider.
This commit is contained in:
Mike Lockwood
2010-06-10 14:21:53 -07:00
committed by Android (Google) Code Review
12 changed files with 185 additions and 14 deletions

View File

@@ -151,6 +151,12 @@ public final class Mtp
*/
public static final String THUMB_HEIGHT = "thumb_height";
/**
* The object's thumbnail.
* <P>Type: BLOB</P>
*/
public static final String THUMB = "thumb";
/**
* The width of the object in pixels.
* <P>Type: INTEGER</P>

View File

@@ -159,6 +159,7 @@ public final class MtpCursor extends AbstractWindowedCursor {
private static final int OBJECT_DATE_CREATED = 218;
private static final int OBJECT_DATE_MODIFIED = 219;
private static final int OBJECT_KEYWORDS = 220;
private static final int OBJECT_THUMB = 221;
private static HashMap<String, Integer> sDeviceProjectionMap;
private static HashMap<String, Integer> sStorageProjectionMap;
@@ -196,6 +197,7 @@ public final class MtpCursor extends AbstractWindowedCursor {
sObjectProjectionMap.put(Mtp.Object.DATE_CREATED, new Integer(OBJECT_DATE_CREATED));
sObjectProjectionMap.put(Mtp.Object.DATE_MODIFIED, new Integer(OBJECT_DATE_MODIFIED));
sObjectProjectionMap.put(Mtp.Object.KEYWORDS, new Integer(OBJECT_KEYWORDS));
sObjectProjectionMap.put(Mtp.Object.THUMB, new Integer(OBJECT_THUMB));
sObjectProjectionMap.put(Mtp.Object.NAME, new Integer(OBJECT_NAME));
}

View File

@@ -62,6 +62,7 @@ namespace android {
#define OBJECT_DATE_CREATED 218
#define OBJECT_DATE_MODIFIED 219
#define OBJECT_KEYWORDS 220
#define OBJECT_THUMB 221
MtpCursor::MtpCursor(MtpClient* client, int queryType, int deviceID,
int storageID, int objectID, int columnCount, int* columns)
@@ -364,6 +365,10 @@ bool MtpCursor::fillObject(CursorWindow* window, MtpDevice* device,
if (!putString(window, objectInfo->mKeywords, row, i))
goto fail;
break;
case OBJECT_THUMB:
if (!putThumbnail(window, objectID, row, i))
goto fail;
break;
default:
LOGE("fillStorage: unknown column %d\n", mColumns[i]);
goto fail;
@@ -421,4 +426,28 @@ bool MtpCursor::putString(CursorWindow* window, const char* text, int row, int c
return true;
}
bool MtpCursor::putThumbnail(CursorWindow* window, int objectID, int row, int column) {
MtpDevice* device = mClient->getDevice(mDeviceID);
int size;
void* thumbnail = device->getThumbnail(objectID, size);
LOGD("putThumbnail: %p, size: %d\n", thumbnail, size);
int offset = window->alloc(size);
if (!offset) {
window->freeLastRow();
LOGE("Failed allocating %u bytes for thumbnail", size);
return false;
}
if (size > 0)
window->copyIn(offset, (const uint8_t*)thumbnail, size);
// This must be updated after the call to alloc(), since that
// may move the field around in the window
field_slot_t * fieldSlot = window->getFieldSlot(row, column);
fieldSlot->type = FIELD_TYPE_BLOB;
fieldSlot->data.buffer.offset = offset;
fieldSlot->data.buffer.size = size;
return true;
}
} // namespace android

View File

@@ -68,6 +68,7 @@ private:
bool prepareRow(CursorWindow* window);
bool putLong(CursorWindow* window, int value, int row, int column);
bool putString(CursorWindow* window, const char* text, int row, int column);
bool putThumbnail(CursorWindow* window, int objectID, int row, int column);
};
}; // namespace android

View File

@@ -361,11 +361,24 @@ int MtpDataPacket::writeDataHeader(int fd, uint32_t length) {
#ifdef MTP_HOST
int MtpDataPacket::read(struct usb_endpoint *ep) {
// first read the header
int ret = transfer(ep, mBuffer, mBufferSize);
printf("MtpDataPacket::transfer returned %d\n", ret);
if (ret >= 0)
mPacketSize = ret;
return ret;
int length = transfer(ep, mBuffer, mBufferSize);
if (length > MTP_CONTAINER_HEADER_SIZE) {
// look at the length field to see if the data spans multiple packets
uint32_t totalLength = MtpPacket::getUInt32(MTP_CONTAINER_LENGTH_OFFSET);
while (totalLength > length) {
allocate(length + mAllocationIncrement);
int ret = transfer(ep, mBuffer + length, mAllocationIncrement);
if (ret >= 0)
length += ret;
else {
length = ret;
break;
}
}
}
if (length >= 0)
mPacketSize = length;
return length;
}
int MtpDataPacket::write(struct usb_endpoint *ep) {
@@ -382,4 +395,18 @@ int MtpDataPacket::write(struct usb_endpoint *ep) {
#endif // MTP_HOST
void* MtpDataPacket::getData(int& outLength) const {
int length = mPacketSize - MTP_CONTAINER_HEADER_SIZE;
if (length > 0) {
void* result = malloc(length);
if (result) {
memcpy(result, mBuffer + MTP_CONTAINER_HEADER_SIZE, length);
outLength = length;
return result;
}
}
outLength = 0;
return NULL;
}
} // namespace android

View File

@@ -99,6 +99,7 @@ public:
#endif
inline bool hasData() const { return mPacketSize > MTP_CONTAINER_HEADER_SIZE; }
void* getData(int& outLength) const;
};
}; // namespace android

View File

@@ -194,6 +194,20 @@ MtpObjectInfo* MtpDevice::getObjectInfo(MtpObjectHandle handle) {
return NULL;
}
void* MtpDevice::getThumbnail(MtpObjectHandle handle, int& outLength) {
mRequest.reset();
mRequest.setParameter(1, handle);
if (sendRequest(MTP_OPERATION_GET_THUMB) && readData()) {
MtpResponseCode ret = readResponse();
if (ret == MTP_RESPONSE_OK) {
return mData.getData(outLength);
}
}
outLength = 0;
return NULL;
}
MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
mRequest.reset();
mRequest.setParameter(1, code);

View File

@@ -70,6 +70,7 @@ public:
MtpStorageInfo* getStorageInfo(MtpStorageID storageID);
MtpObjectHandleList* getObjectHandles(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent);
MtpObjectInfo* getObjectInfo(MtpObjectHandle handle);
void* getThumbnail(MtpObjectHandle handle, int& outLength);
MtpProperty* getDevicePropDesc(MtpDeviceProperty code);

View File

@@ -140,5 +140,8 @@
<TextView android:id="@+id/keywords"
style="@style/info_value" />
</TableRow>
<TableRow>
<ImageView android:id="@+id/thumbnail" />
</TableRow>
</TableLayout>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView android:id="@+id/thumbnail"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="center_vertical"
android:paddingLeft="6dip"
android:minHeight="?android:attr/listPreferredItemHeight" />
</LinearLayout>

View File

@@ -17,16 +17,21 @@
package com.android.camerabrowser;
import android.app.ListActivity;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Mtp;
import android.util.Log;
import android.view.View;
import android.widget.ListAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.ResourceCursorAdapter;
import android.widget.TextView;
/**
* A list view displaying all objects within a container (folder or storage unit).
@@ -41,7 +46,12 @@ public class ObjectBrowser extends ListActivity {
private int mObjectID;
private static final String[] OBJECT_COLUMNS =
new String[] { Mtp.Object._ID, Mtp.Object.NAME, Mtp.Object.FORMAT };
new String[] { Mtp.Object._ID, Mtp.Object.NAME, Mtp.Object.FORMAT, Mtp.Object.THUMB };
static final int ID_COLUMN = 0;
static final int NAME_COLUMN = 1;
static final int FORMAT_COLUMN = 2;
static final int THUMB_COLUMN = 3;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -70,10 +80,7 @@ public class ObjectBrowser extends ListActivity {
startManagingCursor(c);
// Map Cursor columns to views defined in simple_list_item_1.xml
mAdapter = new SimpleCursorAdapter(this,
android.R.layout.simple_list_item_1, c,
new String[] { Mtp.Object.NAME },
new int[] { android.R.id.text1, android.R.id.text2 });
mAdapter = new ObjectCursorAdapter(this, c);
setListAdapter(mAdapter);
}
}
@@ -88,9 +95,9 @@ public class ObjectBrowser extends ListActivity {
long format = 0;
if (c != null && c.getCount() == 1) {
c.moveToFirst();
long rowId = c.getLong(0);
String name = c.getString(1);
format = c.getLong(2);
long rowId = c.getLong(ID_COLUMN);
String name = c.getString(NAME_COLUMN);
format = c.getLong(FORMAT_COLUMN);
Log.d(TAG, "rowId: " + rowId + " name: " + name + " format: " + format);
}
if (format == Mtp.Object.FORMAT_JFIF) {
@@ -107,4 +114,33 @@ public class ObjectBrowser extends ListActivity {
startActivity(intent);
}
}
private class ObjectCursorAdapter extends ResourceCursorAdapter {
public ObjectCursorAdapter(Context context, Cursor c) {
super(context, R.layout.object_list, c);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
ImageView thumbView = (ImageView)view.findViewById(R.id.thumbnail);
TextView nameView = (TextView)view.findViewById(R.id.name);
// get the thumbnail
byte[] thumbnail = cursor.getBlob(THUMB_COLUMN);
if (thumbnail != null) {
Bitmap bitmap = BitmapFactory.decodeByteArray(thumbnail, 0, thumbnail.length);
if (bitmap != null) {
thumbView.setImageBitmap(bitmap);
}
}
// get the name
String name = cursor.getString(NAME_COLUMN);
if (name == null) {
name = "";
}
nameView.setText(name);
}
}
}

View File

@@ -18,11 +18,14 @@ package com.android.camerabrowser;
import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Mtp;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.Date;
@@ -52,6 +55,7 @@ public class ObjectViewer extends Activity {
Mtp.Object.DATE_CREATED,
Mtp.Object.DATE_MODIFIED,
Mtp.Object.KEYWORDS,
Mtp.Object.THUMB,
};
@Override
@@ -100,6 +104,20 @@ public class ObjectViewer extends Activity {
view.setText(date.toString());
view = (TextView)findViewById(R.id.keywords);
view.setText(c.getString(12));
byte[] thumbnail = c.getBlob(13);
if (thumbnail != null) {
Log.d(TAG, "got thumbnail, length: " + thumbnail.length);
for (int i = 0; i < 50; i++) {
Log.d(TAG, " " + Integer.toHexString(thumbnail[i]));
}
ImageView thumbView = (ImageView)findViewById(R.id.thumbnail);
Bitmap bitmap = BitmapFactory.decodeByteArray(thumbnail, 0, thumbnail.length);
Log.d(TAG, "bitmap: " + bitmap);
if (bitmap != null) {
thumbView.setImageBitmap(bitmap);
}
}
}
}
}