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:
committed by
Android (Google) Code Review
commit
aefed3626e
@@ -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>
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -99,6 +99,7 @@ public:
|
||||
#endif
|
||||
|
||||
inline bool hasData() const { return mPacketSize > MTP_CONTAINER_HEADER_SIZE; }
|
||||
void* getData(int& outLength) const;
|
||||
};
|
||||
|
||||
}; // namespace android
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -140,5 +140,8 @@
|
||||
<TextView android:id="@+id/keywords"
|
||||
style="@style/info_value" />
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<ImageView android:id="@+id/thumbnail" />
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
|
||||
|
||||
33
media/tests/CameraBrowser/res/layout/object_list.xml
Normal file
33
media/tests/CameraBrowser/res/layout/object_list.xml
Normal 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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user