Increase the photo size to 480x480 or 720x720, depending on device
Bug:6202229
Change-Id: I98b38023585d154eccad302578f796a2318fd5b2
diff --git a/res/values/config.xml b/res/values/config.xml
deleted file mode 100644
index 426fcc2..0000000
--- a/res/values/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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.
--->
-
-<resources>
-
- <!--
- Maximum dimension (height or width) of contact display photos or
- photos from the social stream.
- -->
- <integer name="config_max_display_photo_dim">256</integer>
-
- <!-- Maximum dimension (height or width) of contact photo thumbnails -->
- <integer name="config_max_thumbnail_photo_dim">96</integer>
-
-</resources>
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java
index dfbfebe..1cba421 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -1390,11 +1390,8 @@
StrictMode.setThreadPolicy(
new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
- Resources resources = getContext().getResources();
- mMaxDisplayPhotoDim = resources.getInteger(
- R.integer.config_max_display_photo_dim);
- mMaxThumbnailPhotoDim = resources.getInteger(
- R.integer.config_max_thumbnail_photo_dim);
+ mMaxThumbnailPhotoDim = PhotoProcessor.getMaxThumbnailSize();
+ mMaxDisplayPhotoDim = PhotoProcessor.getMaxDisplayPhotoSize();
mFastScrollingIndexCache = new FastScrollingIndexCache(getContext());
diff --git a/src/com/android/providers/contacts/DataRowHandlerForPhoto.java b/src/com/android/providers/contacts/DataRowHandlerForPhoto.java
index bfe1c3d..79f9267 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForPhoto.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForPhoto.java
@@ -19,10 +19,8 @@
import android.content.ContentValues;
import android.content.Context;
-import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
-import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.util.Log;
@@ -143,10 +141,8 @@
private boolean processPhoto(ContentValues values) {
byte[] originalPhoto = values.getAsByteArray(Photo.PHOTO);
if (originalPhoto != null) {
- int maxDisplayPhotoDim = mContext.getResources().getInteger(
- R.integer.config_max_display_photo_dim);
- int maxThumbnailPhotoDim = mContext.getResources().getInteger(
- R.integer.config_max_thumbnail_photo_dim);
+ int maxDisplayPhotoDim = PhotoProcessor.getMaxDisplayPhotoSize();
+ int maxThumbnailPhotoDim = PhotoProcessor.getMaxThumbnailSize();
try {
PhotoProcessor processor = new PhotoProcessor(
originalPhoto, maxDisplayPhotoDim, maxThumbnailPhotoDim);
diff --git a/src/com/android/providers/contacts/PhotoProcessor.java b/src/com/android/providers/contacts/PhotoProcessor.java
index d2a33d2..29b3c9d 100644
--- a/src/com/android/providers/contacts/PhotoProcessor.java
+++ b/src/com/android/providers/contacts/PhotoProcessor.java
@@ -15,9 +15,12 @@
*/
package com.android.providers.contacts;
+import com.android.providers.contacts.util.MemoryUtils;
+
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
+import android.os.SystemProperties;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -28,6 +31,38 @@
*/
/* package-protected */ final class PhotoProcessor {
+ /**
+ * The default sizes of a thumbnail/display picture. This is used in {@link #initialize()}
+ */
+ private interface PhotoSizes {
+ /** Size of a thumbnail */
+ public static final int DEFAULT_THUMBNAIL = 96;
+
+ /**
+ * Size of a display photo on memory constrained devices (those are devices with less than
+ * {@link #DEFAULT_LARGE_RAM_THRESHOLD} of reported RAM
+ */
+ public static final int DEFAULT_DISPLAY_PHOTO_MEMORY_CONSTRAINED = 480;
+
+ /**
+ * Size of a display photo on devices with enough ram (those are devices with at least
+ * {@link #DEFAULT_LARGE_RAM_THRESHOLD} of reported RAM
+ */
+ public static final int DEFAULT_DISPLAY_PHOTO_LARGE_MEMORY = 720;
+
+ /**
+ * If the device has less than this amount of RAM, it is considered RAM constrained for
+ * photos
+ */
+ public static final int LARGE_RAM_THRESHOLD = 640 * 1024 * 1024;
+
+ /** If present, overrides the size given in {@link #DEFAULT_THUMBNAIL} */
+ public static final String SYS_PROPERTY_THUMBNAIL_SIZE = "contacts.thumbnail_size";
+
+ /** If present, overrides the size determined for the display photo */
+ public static final String SYS_PROPERTY_DISPLAY_PHOTO_SIZE = "contacts.display_photo_size";
+ }
+
private final int mMaxDisplayPhotoDim;
private final int mMaxThumbnailPhotoDim;
private final boolean mForceCropToSquare;
@@ -139,7 +174,7 @@
Matrix matrix = new Matrix();
if (scaleFactor < 1.0f) matrix.setScale(scaleFactor, scaleFactor);
scaledBitmap = Bitmap.createBitmap(
- mOriginal, cropLeft, cropTop, width, height, matrix, false);
+ mOriginal, cropLeft, cropTop, width, height, matrix, true);
}
return scaledBitmap;
}
@@ -147,15 +182,17 @@
/**
* Helper method to compress the given bitmap as a JPEG and return the resulting byte array.
*/
- private byte[] getCompressedBytes(Bitmap b) throws IOException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- boolean compressed = b.compress(Bitmap.CompressFormat.JPEG, 95, baos);
+ private byte[] getCompressedBytes(Bitmap b, int quality) throws IOException {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ final boolean compressed = b.compress(Bitmap.CompressFormat.JPEG, quality, baos);
+ baos.flush();
+ baos.close();
+ byte[] result = baos.toByteArray();
+
if (!compressed) {
throw new IOException("Unable to compress image");
}
- baos.flush();
- baos.close();
- return baos.toByteArray();
+ return result;
}
/**
@@ -176,14 +213,19 @@
* Retrieves the compressed display photo as a byte array.
*/
public byte[] getDisplayPhotoBytes() throws IOException {
- return getCompressedBytes(mDisplayPhoto);
+ return getCompressedBytes(mDisplayPhoto, 75);
}
/**
* Retrieves the compressed thumbnail photo as a byte array.
*/
public byte[] getThumbnailPhotoBytes() throws IOException {
- return getCompressedBytes(mThumbnailPhoto);
+ // If there is a higher-resolution picture, we can assume we won't need to upscale the
+ // thumbnail often, so we can compress stronger
+ final boolean hasDisplayPhoto = mDisplayPhoto != null &&
+ (mDisplayPhoto.getWidth() > mThumbnailPhoto.getWidth() ||
+ mDisplayPhoto.getHeight() > mThumbnailPhoto.getHeight());
+ return getCompressedBytes(mThumbnailPhoto, hasDisplayPhoto ? 90 : 95);
}
/**
@@ -199,4 +241,30 @@
public int getMaxThumbnailPhotoDim() {
return mMaxThumbnailPhotoDim;
}
+
+ /**
+ * Returns the maximum size in pixel of a thumbnail (which has a default that can be overriden
+ * using a system-property)
+ */
+ public static int getMaxThumbnailSize() {
+ final int sysPropThumbSize =
+ SystemProperties.getInt(PhotoSizes.SYS_PROPERTY_THUMBNAIL_SIZE, -1);
+ return sysPropThumbSize == -1 ? PhotoSizes.DEFAULT_THUMBNAIL : sysPropThumbSize;
+ }
+
+ /**
+ * Returns the maximum size in pixel of a display photo (which is determined based
+ * on available RAM or configured using a system-property)
+ */
+ public static int getMaxDisplayPhotoSize() {
+ final int sysPropDisplaySize =
+ SystemProperties.getInt(PhotoSizes.SYS_PROPERTY_DISPLAY_PHOTO_SIZE, -1);
+ if (sysPropDisplaySize != -1) return sysPropDisplaySize;
+
+ final boolean isExpensiveDevice =
+ MemoryUtils.getTotalMemorySize() >= PhotoSizes.LARGE_RAM_THRESHOLD;
+ return isExpensiveDevice
+ ? PhotoSizes.DEFAULT_DISPLAY_PHOTO_LARGE_MEMORY
+ : PhotoSizes.DEFAULT_DISPLAY_PHOTO_MEMORY_CONSTRAINED;
+ }
}
diff --git a/src/com/android/providers/contacts/PhotoStore.java b/src/com/android/providers/contacts/PhotoStore.java
index 5f4b11c..e0b5fb4 100644
--- a/src/com/android/providers/contacts/PhotoStore.java
+++ b/src/com/android/providers/contacts/PhotoStore.java
@@ -194,7 +194,7 @@
byte[] photoBytes = photoProcessor.getDisplayPhotoBytes();
file = File.createTempFile("img", null, mStorePath);
FileOutputStream fos = new FileOutputStream(file);
- fos.write(photoProcessor.getDisplayPhotoBytes());
+ fos.write(photoBytes);
fos.close();
// Create the DB entry.
diff --git a/src/com/android/providers/contacts/util/MemoryUtils.java b/src/com/android/providers/contacts/util/MemoryUtils.java
new file mode 100644
index 0000000..2ad9c0b
--- /dev/null
+++ b/src/com/android/providers/contacts/util/MemoryUtils.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+package com.android.providers.contacts.util;
+
+import com.android.internal.util.MemInfoReader;
+
+public final class MemoryUtils {
+ private MemoryUtils() {
+ }
+
+ private static long sTotalMemorySize = -1;
+
+ /**
+ * Returns the amount of RAM available to the Linux kernel, i.e. whatever is left after all the
+ * other chips stake their claim, including GPUs, DSPs, cell radios, and any other greedy chips
+ * (in other words: this is probably less than the "RAM: 1 GB" that was printed on the
+ * box in far too big letters)
+ */
+ public static long getTotalMemorySize() {
+ if (sTotalMemorySize < 0) {
+ MemInfoReader reader = new MemInfoReader();
+ reader.readMemInfo();
+
+ // getTotalSize() returns the "MemTotal" value from /proc/meminfo.
+ sTotalMemorySize = reader.getTotalSize();
+ }
+ return sTotalMemorySize;
+ }
+}
diff --git a/tests/src/com/android/providers/contacts/PhotoLoadingTestCase.java b/tests/src/com/android/providers/contacts/PhotoLoadingTestCase.java
index 459fec0..3b7bfc4 100644
--- a/tests/src/com/android/providers/contacts/PhotoLoadingTestCase.java
+++ b/tests/src/com/android/providers/contacts/PhotoLoadingTestCase.java
@@ -44,10 +44,9 @@
Map<PhotoSize, byte[]> photoMap = Maps.newHashMap();
public PhotoEntry(byte[] original) {
try {
- Resources resources = getContext().getResources();
PhotoProcessor processor = new PhotoProcessor(original,
- resources.getInteger(R.integer.config_max_display_photo_dim),
- resources.getInteger(R.integer.config_max_thumbnail_photo_dim));
+ PhotoProcessor.getMaxDisplayPhotoSize(),
+ PhotoProcessor.getMaxThumbnailSize());
photoMap.put(PhotoSize.ORIGINAL, original);
photoMap.put(PhotoSize.DISPLAY_PHOTO, processor.getDisplayPhotoBytes());
photoMap.put(PhotoSize.THUMBNAIL, processor.getThumbnailPhotoBytes());