* commit '548f4d6c7089dc262f47b18542566b0a72807ac6': Icon: a clean, parcelable place for images.
This commit is contained in:
@@ -12486,6 +12486,25 @@ package android.graphics.drawable {
|
||||
enum_constant public static final android.graphics.drawable.GradientDrawable.Orientation TR_BL;
|
||||
}
|
||||
|
||||
public final class Icon implements android.os.Parcelable {
|
||||
method public static android.graphics.drawable.Icon createWithBitmap(android.graphics.Bitmap);
|
||||
method public static android.graphics.drawable.Icon createWithContentUri(java.lang.String);
|
||||
method public static android.graphics.drawable.Icon createWithContentUri(android.net.Uri);
|
||||
method public static android.graphics.drawable.Icon createWithData(byte[], int, int);
|
||||
method public static android.graphics.drawable.Icon createWithFilePath(java.lang.String);
|
||||
method public static android.graphics.drawable.Icon createWithResource(android.content.res.Resources, int);
|
||||
method public int describeContents();
|
||||
method public android.graphics.drawable.Drawable loadDrawable(android.content.Context);
|
||||
method public void loadDrawableAsync(android.content.Context, android.os.Message);
|
||||
method public void loadDrawableAsync(android.content.Context, android.os.Handler, android.graphics.drawable.Icon.OnDrawableLoadedListener);
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.graphics.drawable.Icon> CREATOR;
|
||||
}
|
||||
|
||||
public static abstract interface Icon.OnDrawableLoadedListener {
|
||||
method public abstract void onDrawableLoaded(android.graphics.drawable.Drawable);
|
||||
}
|
||||
|
||||
public class InsetDrawable extends android.graphics.drawable.DrawableWrapper {
|
||||
ctor public InsetDrawable(android.graphics.drawable.Drawable, int);
|
||||
ctor public InsetDrawable(android.graphics.drawable.Drawable, int, int, int, int);
|
||||
|
||||
@@ -12780,6 +12780,25 @@ package android.graphics.drawable {
|
||||
enum_constant public static final android.graphics.drawable.GradientDrawable.Orientation TR_BL;
|
||||
}
|
||||
|
||||
public final class Icon implements android.os.Parcelable {
|
||||
method public static android.graphics.drawable.Icon createWithBitmap(android.graphics.Bitmap);
|
||||
method public static android.graphics.drawable.Icon createWithContentUri(java.lang.String);
|
||||
method public static android.graphics.drawable.Icon createWithContentUri(android.net.Uri);
|
||||
method public static android.graphics.drawable.Icon createWithData(byte[], int, int);
|
||||
method public static android.graphics.drawable.Icon createWithFilePath(java.lang.String);
|
||||
method public static android.graphics.drawable.Icon createWithResource(android.content.res.Resources, int);
|
||||
method public int describeContents();
|
||||
method public android.graphics.drawable.Drawable loadDrawable(android.content.Context);
|
||||
method public void loadDrawableAsync(android.content.Context, android.os.Message);
|
||||
method public void loadDrawableAsync(android.content.Context, android.os.Handler, android.graphics.drawable.Icon.OnDrawableLoadedListener);
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.graphics.drawable.Icon> CREATOR;
|
||||
}
|
||||
|
||||
public static abstract interface Icon.OnDrawableLoadedListener {
|
||||
method public abstract void onDrawableLoaded(android.graphics.drawable.Drawable);
|
||||
}
|
||||
|
||||
public class InsetDrawable extends android.graphics.drawable.DrawableWrapper {
|
||||
ctor public InsetDrawable(android.graphics.drawable.Drawable, int);
|
||||
ctor public InsetDrawable(android.graphics.drawable.Drawable, int, int, int, int);
|
||||
|
||||
@@ -502,7 +502,25 @@ public final class Parcel {
|
||||
* {@SystemApi}
|
||||
*/
|
||||
public final void writeBlob(byte[] b) {
|
||||
nativeWriteBlob(mNativePtr, b, 0, (b != null) ? b.length : 0);
|
||||
writeBlob(b, 0, (b != null) ? b.length : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a blob of data into the parcel at the current {@link #dataPosition},
|
||||
* growing {@link #dataCapacity} if needed.
|
||||
* @param b Bytes to place into the parcel.
|
||||
* @param offset Index of first byte to be written.
|
||||
* @param len Number of bytes to write.
|
||||
* {@hide}
|
||||
* {@SystemApi}
|
||||
*/
|
||||
public final void writeBlob(byte[] b, int offset, int len) {
|
||||
if (b == null) {
|
||||
writeInt(-1);
|
||||
return;
|
||||
}
|
||||
Arrays.checkOffsetAndCount(b.length, offset, len);
|
||||
nativeWriteBlob(mNativePtr, b, offset, len);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
482
graphics/java/android/graphics/drawable/Icon.java
Normal file
482
graphics/java/android/graphics/drawable/Icon.java
Normal file
@@ -0,0 +1,482 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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 android.graphics.drawable;
|
||||
|
||||
import android.annotation.DrawableRes;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.IllegalArgumentException;
|
||||
import java.lang.Override;
|
||||
|
||||
/**
|
||||
* An umbrella container for several serializable graphics representations, including Bitmaps,
|
||||
* compressed bitmap images (e.g. JPG or PNG), and drawable resources (including vectors).
|
||||
*
|
||||
* <a href="https://developer.android.com/training/displaying-bitmaps/index.html">Much ink</a>
|
||||
* has been spilled on the best way to load images, and many clients may have different needs when
|
||||
* it comes to threading and fetching. This class is therefore focused on encapsulation rather than
|
||||
* behavior.
|
||||
*/
|
||||
|
||||
public final class Icon implements Parcelable {
|
||||
private static final String TAG = "Icon";
|
||||
|
||||
private static final int TYPE_BITMAP = 1;
|
||||
private static final int TYPE_RESOURCE = 2;
|
||||
private static final int TYPE_DATA = 3;
|
||||
private static final int TYPE_URI = 4;
|
||||
|
||||
private final int mType;
|
||||
|
||||
// To avoid adding unnecessary overhead, we have a few basic objects that get repurposed
|
||||
// based on the value of mType.
|
||||
|
||||
// TYPE_BITMAP: Bitmap
|
||||
// TYPE_RESOURCE: Resources
|
||||
// TYPE_DATA: DataBytes
|
||||
private Object mObj1;
|
||||
|
||||
// TYPE_RESOURCE: package name
|
||||
// TYPE_URI: uri string
|
||||
private String mString1;
|
||||
|
||||
// TYPE_RESOURCE: resId
|
||||
// TYPE_DATA: data length
|
||||
private int mInt1;
|
||||
|
||||
// TYPE_DATA: data offset
|
||||
private int mInt2;
|
||||
|
||||
// Internal accessors for different mType variants
|
||||
private Bitmap getBitmap() {
|
||||
if (mType != TYPE_BITMAP) {
|
||||
throw new IllegalStateException("called getBitmap() on " + this);
|
||||
}
|
||||
return (Bitmap) mObj1;
|
||||
}
|
||||
|
||||
private int getDataLength() {
|
||||
if (mType != TYPE_DATA) {
|
||||
throw new IllegalStateException("called getDataLength() on " + this);
|
||||
}
|
||||
synchronized (this) {
|
||||
return mInt1;
|
||||
}
|
||||
}
|
||||
|
||||
private int getDataOffset() {
|
||||
if (mType != TYPE_DATA) {
|
||||
throw new IllegalStateException("called getDataOffset() on " + this);
|
||||
}
|
||||
synchronized (this) {
|
||||
return mInt2;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] getDataBytes() {
|
||||
if (mType != TYPE_DATA) {
|
||||
throw new IllegalStateException("called getDataBytes() on " + this);
|
||||
}
|
||||
synchronized (this) {
|
||||
return (byte[]) mObj1;
|
||||
}
|
||||
}
|
||||
|
||||
private Resources getResources() {
|
||||
if (mType != TYPE_RESOURCE) {
|
||||
throw new IllegalStateException("called getResources() on " + this);
|
||||
}
|
||||
return (Resources) mObj1;
|
||||
}
|
||||
|
||||
private String getResPackage() {
|
||||
if (mType != TYPE_RESOURCE) {
|
||||
throw new IllegalStateException("called getResPackage() on " + this);
|
||||
}
|
||||
return mString1;
|
||||
}
|
||||
|
||||
private int getResId() {
|
||||
if (mType != TYPE_RESOURCE) {
|
||||
throw new IllegalStateException("called getResId() on " + this);
|
||||
}
|
||||
return mInt1;
|
||||
}
|
||||
|
||||
private String getUriString() {
|
||||
if (mType != TYPE_URI) {
|
||||
throw new IllegalStateException("called getUriString() on " + this);
|
||||
}
|
||||
return mString1;
|
||||
}
|
||||
|
||||
private Uri getUri() {
|
||||
return Uri.parse(getUriString());
|
||||
}
|
||||
|
||||
// Convert a int32 into a four-char string
|
||||
private static final String typeToString(int x) {
|
||||
switch (x) {
|
||||
case TYPE_BITMAP: return "BITMAP";
|
||||
case TYPE_DATA: return "DATA";
|
||||
case TYPE_RESOURCE: return "RESOURCE";
|
||||
case TYPE_URI: return "URI";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link #loadDrawable(Context)} on the given {@link android.os.Handler Handler}
|
||||
* and then sends <code>andThen</code> to the same Handler when finished.
|
||||
*
|
||||
* @param context {@link android.content.Context Context} in which to load the drawable; see
|
||||
* {@link #loadDrawable(Context)}
|
||||
* @param andThen {@link android.os.Message} to send to its target once the drawable
|
||||
* is available. The {@link android.os.Message#obj obj}
|
||||
* property is populated with the Drawable.
|
||||
*/
|
||||
public void loadDrawableAsync(Context context, Message andThen) {
|
||||
if (andThen.getTarget() == null) {
|
||||
throw new IllegalArgumentException("callback message must have a target handler");
|
||||
}
|
||||
new LoadDrawableTask(context, andThen).runAsync();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link #loadDrawable(Context)} on a background thread
|
||||
* and then runs <code>andThen</code> on the UI thread when finished.
|
||||
*
|
||||
* @param context {@link android.content.Context Context} in which to load the drawable; see
|
||||
* {@link #loadDrawable(Context)}
|
||||
* @param handler {@link android.os.Handler} on which to run <code>andThen</code>.
|
||||
* @param listener a callback to run on the provided
|
||||
* Handler once the drawable is available.
|
||||
*/
|
||||
public void loadDrawableAsync(Context context, Handler handler,
|
||||
final OnDrawableLoadedListener listener) {
|
||||
new LoadDrawableTask(context, handler, listener).runAsync();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Drawable that can be used to draw the image inside this Icon, constructing it
|
||||
* if necessary. Depending on the type of image, this may not be something you want to do on
|
||||
* the UI thread, so consider using
|
||||
* {@link #loadDrawableAsync(Context, Message) loadDrawableAsync} instead.
|
||||
*
|
||||
* @param context {@link android.content.Context Context} in which to load the drawable; used
|
||||
* to access {@link android.content.res.Resources Resources}, for example.
|
||||
* @return A fresh instance of a drawable for this image, yours to keep.
|
||||
*/
|
||||
public Drawable loadDrawable(Context context) {
|
||||
switch (mType) {
|
||||
case TYPE_BITMAP:
|
||||
return new BitmapDrawable(context.getResources(), getBitmap());
|
||||
case TYPE_RESOURCE:
|
||||
if (getResources() == null) {
|
||||
if (getResPackage() == null || "android".equals(getResPackage())) {
|
||||
mObj1 = Resources.getSystem();
|
||||
} else {
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
try {
|
||||
mObj1 = pm.getResourcesForApplication(getResPackage());
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(TAG,
|
||||
String.format("Unable to find package '%s'", getResPackage()),
|
||||
e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return getResources().getDrawable(getResId(), context.getTheme());
|
||||
case TYPE_DATA:
|
||||
return new BitmapDrawable(context.getResources(),
|
||||
BitmapFactory.decodeByteArray(getDataBytes(), getDataOffset(), getDataLength())
|
||||
);
|
||||
case TYPE_URI:
|
||||
final Uri uri = getUri();
|
||||
final String scheme = uri.getScheme();
|
||||
InputStream is = null;
|
||||
if (ContentResolver.SCHEME_CONTENT.equals(scheme)
|
||||
|| ContentResolver.SCHEME_FILE.equals(scheme)) {
|
||||
try {
|
||||
is = context.getContentResolver().openInputStream(uri);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Unable to load image from URI: " + uri, e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
is = new FileInputStream(new File(mString1));
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.w(TAG, "Unable to load image from path: " + uri, e);
|
||||
}
|
||||
}
|
||||
if (is != null) {
|
||||
return new BitmapDrawable(context.getResources(),
|
||||
BitmapFactory.decodeStream(is));
|
||||
}
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Icon(int mType) {
|
||||
this.mType = mType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Icon pointing to a drawable resource.
|
||||
* @param res Resources for a package containing the resource in question
|
||||
* @param resid ID of the drawable resource
|
||||
*/
|
||||
public static Icon createWithResource(Resources res, @DrawableRes int resid) {
|
||||
final Icon rep = new Icon(TYPE_RESOURCE);
|
||||
rep.mObj1 = res;
|
||||
rep.mInt1 = resid;
|
||||
rep.mString1 = res.getResourcePackageName(resid);
|
||||
return rep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Icon pointing to a bitmap in memory.
|
||||
* @param bits A valid {@link android.graphics.Bitmap} object
|
||||
*/
|
||||
public static Icon createWithBitmap(Bitmap bits) {
|
||||
final Icon rep = new Icon(TYPE_BITMAP);
|
||||
rep.mObj1 = bits;
|
||||
return rep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Icon pointing to a compressed bitmap stored in a byte array.
|
||||
* @param data Byte array storing compressed bitmap data of a type that
|
||||
* {@link android.graphics.BitmapFactory}
|
||||
* can decode (see {@link android.graphics.Bitmap.CompressFormat}).
|
||||
* @param offset Offset into <code>data</code> at which the bitmap data starts
|
||||
* @param length Length of the bitmap data
|
||||
*/
|
||||
public static Icon createWithData(byte[] data, int offset, int length) {
|
||||
final Icon rep = new Icon(TYPE_DATA);
|
||||
rep.mObj1 = data;
|
||||
rep.mInt1 = length;
|
||||
rep.mInt2 = offset;
|
||||
return rep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Icon pointing to a content specified by URI.
|
||||
*
|
||||
* @param uri A uri referring to local content:// or file:// image data.
|
||||
*/
|
||||
public static Icon createWithContentUri(String uri) {
|
||||
final Icon rep = new Icon(TYPE_URI);
|
||||
rep.mString1 = uri;
|
||||
return rep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Icon pointing to a content specified by URI.
|
||||
*
|
||||
* @param uri A uri referring to local content:// or file:// image data.
|
||||
*/
|
||||
public static Icon createWithContentUri(Uri uri) {
|
||||
final Icon rep = new Icon(TYPE_URI);
|
||||
rep.mString1 = uri.toString();
|
||||
return rep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Icon pointing to
|
||||
*
|
||||
* @param path A path to a file that contains compressed bitmap data of
|
||||
* a type that {@link android.graphics.BitmapFactory} can decode.
|
||||
*/
|
||||
public static Icon createWithFilePath(String path) {
|
||||
final Icon rep = new Icon(TYPE_URI);
|
||||
rep.mString1 = path;
|
||||
return rep;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("Icon(typ=").append(typeToString(mType));
|
||||
switch (mType) {
|
||||
case TYPE_BITMAP:
|
||||
sb.append(" size=")
|
||||
.append(getBitmap().getWidth())
|
||||
.append("x")
|
||||
.append(getBitmap().getHeight());
|
||||
break;
|
||||
case TYPE_RESOURCE:
|
||||
sb.append(" pkg=")
|
||||
.append(getResPackage())
|
||||
.append(" id=")
|
||||
.append(String.format("%08x", getResId()));
|
||||
break;
|
||||
case TYPE_DATA:
|
||||
sb.append(" len=").append(getDataLength());
|
||||
if (getDataOffset() != 0) {
|
||||
sb.append(" off=").append(getDataOffset());
|
||||
}
|
||||
break;
|
||||
case TYPE_URI:
|
||||
sb.append(" uri=").append(getUriString());
|
||||
break;
|
||||
}
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parcelable interface
|
||||
*/
|
||||
public int describeContents() {
|
||||
return (mType == TYPE_BITMAP || mType == TYPE_DATA)
|
||||
? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0;
|
||||
}
|
||||
|
||||
// ===== Parcelable interface ======
|
||||
|
||||
private Icon(Parcel in) {
|
||||
this(in.readInt());
|
||||
switch (mType) {
|
||||
case TYPE_BITMAP:
|
||||
final Bitmap bits = Bitmap.CREATOR.createFromParcel(in);
|
||||
mObj1 = bits;
|
||||
break;
|
||||
case TYPE_RESOURCE:
|
||||
final String pkg = in.readString();
|
||||
final int resId = in.readInt();
|
||||
mString1 = pkg;
|
||||
mInt1 = resId;
|
||||
break;
|
||||
case TYPE_DATA:
|
||||
final int len = in.readInt();
|
||||
final byte[] a = in.readBlob();
|
||||
if (len != a.length) {
|
||||
throw new RuntimeException("internal unparceling error: blob length ("
|
||||
+ a.length + ") != expected length (" + len + ")");
|
||||
}
|
||||
mInt1 = len;
|
||||
mObj1 = a;
|
||||
break;
|
||||
case TYPE_URI:
|
||||
final String uri = in.readString();
|
||||
mString1 = uri;
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("invalid "
|
||||
+ this.getClass().getSimpleName() + " type in parcel: " + mType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
switch (mType) {
|
||||
case TYPE_BITMAP:
|
||||
final Bitmap bits = getBitmap();
|
||||
dest.writeInt(TYPE_BITMAP);
|
||||
getBitmap().writeToParcel(dest, flags);
|
||||
break;
|
||||
case TYPE_RESOURCE:
|
||||
dest.writeInt(TYPE_RESOURCE);
|
||||
dest.writeString(getResPackage());
|
||||
dest.writeInt(getResId());
|
||||
break;
|
||||
case TYPE_DATA:
|
||||
dest.writeInt(TYPE_DATA);
|
||||
dest.writeInt(getDataLength());
|
||||
dest.writeBlob(getDataBytes(), getDataOffset(), getDataLength());
|
||||
break;
|
||||
case TYPE_URI:
|
||||
dest.writeInt(TYPE_URI);
|
||||
dest.writeString(getUriString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<Icon> CREATOR
|
||||
= new Parcelable.Creator<Icon>() {
|
||||
public Icon createFromParcel(Parcel in) {
|
||||
return new Icon(in);
|
||||
}
|
||||
|
||||
public Icon[] newArray(int size) {
|
||||
return new Icon[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Implement this interface to receive notification when
|
||||
* {@link #loadDrawableAsync(Context, Handler, OnDrawableLoadedListener) loadDrawableAsync}
|
||||
* is finished and your Drawable is ready.
|
||||
*/
|
||||
public interface OnDrawableLoadedListener {
|
||||
void onDrawableLoaded(Drawable d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around loadDrawable that does its work on a pooled thread and then
|
||||
* fires back the given (targeted) Message.
|
||||
*/
|
||||
private class LoadDrawableTask implements Runnable {
|
||||
final Context mContext;
|
||||
final Message mMessage;
|
||||
|
||||
public LoadDrawableTask(Context context, final Handler handler,
|
||||
final OnDrawableLoadedListener listener) {
|
||||
mContext = context;
|
||||
mMessage = Message.obtain(handler, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onDrawableLoaded((Drawable) mMessage.obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public LoadDrawableTask(Context context, Message message) {
|
||||
mContext = context;
|
||||
mMessage = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mMessage.obj = loadDrawable(mContext);
|
||||
mMessage.sendToTarget();
|
||||
}
|
||||
|
||||
public void runAsync() {
|
||||
AsyncTask.THREAD_POOL_EXECUTOR.execute(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@
|
||||
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
|
||||
<uses-permission android:name="android.permission.WRITE_APN_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
|
||||
<application>
|
||||
|
||||
BIN
graphics/tests/graphicstests/res/drawable-nodpi/landscape.png
Normal file
BIN
graphics/tests/graphicstests/res/drawable-nodpi/landscape.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.graphics.drawable;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Parcel;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.lang.Override;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import com.android.frameworks.graphicstests.R;
|
||||
|
||||
public class IconTest extends AndroidTestCase {
|
||||
public static final String TAG = IconTest.class.getSimpleName();
|
||||
public static void L(String s, Object... parts) {
|
||||
Log.d(TAG, (parts.length == 0) ? s : String.format(s, parts));
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testWithBitmap() throws Exception {
|
||||
final Bitmap bm1 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
|
||||
final Bitmap bm2 = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
|
||||
final Bitmap bm3 = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape))
|
||||
.getBitmap();
|
||||
|
||||
final Canvas can1 = new Canvas(bm1);
|
||||
can1.drawColor(0xFFFF0000);
|
||||
final Canvas can2 = new Canvas(bm2);
|
||||
can2.drawColor(0xFF00FF00);
|
||||
|
||||
final Icon im1 = Icon.createWithBitmap(bm1);
|
||||
final Icon im2 = Icon.createWithBitmap(bm2);
|
||||
final Icon im3 = Icon.createWithBitmap(bm3);
|
||||
|
||||
final Drawable draw1 = im1.loadDrawable(mContext);
|
||||
final Drawable draw2 = im2.loadDrawable(mContext);
|
||||
final Drawable draw3 = im3.loadDrawable(mContext);
|
||||
|
||||
final Bitmap test1 = Bitmap.createBitmap(draw1.getIntrinsicWidth(),
|
||||
draw1.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||
final Bitmap test2 = Bitmap.createBitmap(draw2.getIntrinsicWidth(),
|
||||
draw2.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||
final Bitmap test3 = Bitmap.createBitmap(draw3.getIntrinsicWidth(),
|
||||
draw3.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||
|
||||
draw1.setBounds(0, 0, draw1.getIntrinsicWidth(), draw1.getIntrinsicHeight());
|
||||
draw1.draw(new Canvas(test1));
|
||||
|
||||
draw2.setBounds(0, 0, draw2.getIntrinsicWidth(), draw2.getIntrinsicHeight());
|
||||
draw2.draw(new Canvas(test2));
|
||||
|
||||
draw3.setBounds(0, 0, draw3.getIntrinsicWidth(), draw3.getIntrinsicHeight());
|
||||
draw3.draw(new Canvas(test3));
|
||||
|
||||
final File dir = getContext().getExternalFilesDir(null);
|
||||
L("writing temp bitmaps to %s...", dir);
|
||||
|
||||
bm1.compress(Bitmap.CompressFormat.PNG, 100,
|
||||
new FileOutputStream(new File(dir, "bitmap1-original.png")));
|
||||
test1.compress(Bitmap.CompressFormat.PNG, 100,
|
||||
new FileOutputStream(new File(dir, "bitmap1-test.png")));
|
||||
if (!equalBitmaps(bm1, test1)) {
|
||||
findBitmapDifferences(bm1, test1);
|
||||
fail("bitmap1 differs, check " + dir);
|
||||
}
|
||||
|
||||
bm2.compress(Bitmap.CompressFormat.PNG, 100,
|
||||
new FileOutputStream(new File(dir, "bitmap2-original.png")));
|
||||
test2.compress(Bitmap.CompressFormat.PNG, 100,
|
||||
new FileOutputStream(new File(dir, "bitmap2-test.png")));
|
||||
if (!equalBitmaps(bm2, test2)) {
|
||||
findBitmapDifferences(bm2, test2);
|
||||
fail("bitmap2 differs, check " + dir);
|
||||
}
|
||||
|
||||
bm3.compress(Bitmap.CompressFormat.PNG, 100,
|
||||
new FileOutputStream(new File(dir, "bitmap3-original.png")));
|
||||
test3.compress(Bitmap.CompressFormat.PNG, 100,
|
||||
new FileOutputStream(new File(dir, "bitmap3-test.png")));
|
||||
if (!equalBitmaps(bm3, test3)) {
|
||||
findBitmapDifferences(bm3, test3);
|
||||
fail("bitmap3 differs, check " + dir);
|
||||
}
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testWithBitmapResource() throws Exception {
|
||||
final Bitmap res1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape))
|
||||
.getBitmap();
|
||||
|
||||
final Icon im1 = Icon.createWithResource(getContext().getResources(),
|
||||
R.drawable.landscape);
|
||||
final Drawable draw1 = im1.loadDrawable(mContext);
|
||||
final Bitmap test1 = Bitmap.createBitmap(draw1.getIntrinsicWidth(),
|
||||
draw1.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||
draw1.setBounds(0, 0, test1.getWidth(), test1.getHeight());
|
||||
draw1.draw(new Canvas(test1));
|
||||
|
||||
final File dir = getContext().getExternalFilesDir(null);
|
||||
res1.compress(Bitmap.CompressFormat.PNG, 100,
|
||||
new FileOutputStream(new File(dir, "res1-original.png")));
|
||||
test1.compress(Bitmap.CompressFormat.PNG, 100,
|
||||
new FileOutputStream(new File(dir, "res1-test.png")));
|
||||
if (!equalBitmaps(res1, test1)) {
|
||||
findBitmapDifferences(res1, test1);
|
||||
fail("res1 differs, check " + dir);
|
||||
}
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testWithFile() throws Exception {
|
||||
final Bitmap bit1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape))
|
||||
.getBitmap();
|
||||
final File dir = getContext().getExternalFilesDir(null);
|
||||
final File file1 = new File(dir, "file1-original.png");
|
||||
bit1.compress(Bitmap.CompressFormat.PNG, 100,
|
||||
new FileOutputStream(file1));
|
||||
|
||||
final Icon im1 = Icon.createWithFilePath(file1.toString());
|
||||
final Drawable draw1 = im1.loadDrawable(mContext);
|
||||
final Bitmap test1 = Bitmap.createBitmap(draw1.getIntrinsicWidth(),
|
||||
draw1.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||
draw1.setBounds(0, 0, test1.getWidth(), test1.getHeight());
|
||||
draw1.draw(new Canvas(test1));
|
||||
|
||||
test1.compress(Bitmap.CompressFormat.PNG, 100,
|
||||
new FileOutputStream(new File(dir, "file1-test.png")));
|
||||
if (!equalBitmaps(bit1, test1)) {
|
||||
findBitmapDifferences(bit1, test1);
|
||||
fail("testWithFile: file1 differs, check " + dir);
|
||||
}
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testAsync() throws Exception {
|
||||
final Bitmap bit1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape))
|
||||
.getBitmap();
|
||||
final File dir = getContext().getExternalFilesDir(null);
|
||||
final File file1 = new File(dir, "async-original.png");
|
||||
bit1.compress(Bitmap.CompressFormat.PNG, 100,
|
||||
new FileOutputStream(file1));
|
||||
|
||||
final Icon im1 = Icon.createWithFilePath(file1.toString());
|
||||
final HandlerThread thd = new HandlerThread("testAsync");
|
||||
thd.start();
|
||||
final Handler h = new Handler(thd.getLooper());
|
||||
L(TAG, "asyncTest: dispatching load to thread: " + thd);
|
||||
im1.loadDrawableAsync(mContext, h, new Icon.OnDrawableLoadedListener() {
|
||||
@Override
|
||||
public void onDrawableLoaded(Drawable draw1) {
|
||||
L(TAG, "asyncTest: thread: loading drawable");
|
||||
L(TAG, "asyncTest: thread: loaded: %dx%d", draw1.getIntrinsicWidth(),
|
||||
draw1.getIntrinsicHeight());
|
||||
final Bitmap test1 = Bitmap.createBitmap(draw1.getIntrinsicWidth(),
|
||||
draw1.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||
draw1.setBounds(0, 0, test1.getWidth(), test1.getHeight());
|
||||
draw1.draw(new Canvas(test1));
|
||||
|
||||
try {
|
||||
test1.compress(Bitmap.CompressFormat.PNG, 100,
|
||||
new FileOutputStream(new File(dir, "async-test.png")));
|
||||
} catch (java.io.FileNotFoundException ex) {
|
||||
fail("couldn't create test file: " + ex);
|
||||
}
|
||||
if (!equalBitmaps(bit1, test1)) {
|
||||
findBitmapDifferences(bit1, test1);
|
||||
fail("testAsync: file1 differs, check " + dir);
|
||||
}
|
||||
}
|
||||
});
|
||||
L(TAG, "asyncTest: awaiting result");
|
||||
Thread.sleep(500); // ;_;
|
||||
assertTrue("async-test.png does not exist!", new File(dir, "async-test.png").exists());
|
||||
L(TAG, "asyncTest: done");
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testParcel() throws Exception {
|
||||
final Bitmap originalbits = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape))
|
||||
.getBitmap();
|
||||
|
||||
final ByteArrayOutputStream ostream = new ByteArrayOutputStream(
|
||||
originalbits.getWidth() * originalbits.getHeight() * 2); // guess 50% compression
|
||||
originalbits.compress(Bitmap.CompressFormat.PNG, 100, ostream);
|
||||
final byte[] pngdata = ostream.toByteArray();
|
||||
|
||||
L("starting testParcel; bitmap: %d bytes, PNG: %d bytes",
|
||||
originalbits.getByteCount(),
|
||||
pngdata.length);
|
||||
|
||||
final File dir = getContext().getExternalFilesDir(null);
|
||||
final File originalfile = new File(dir, "parcel-original.png");
|
||||
new FileOutputStream(originalfile).write(pngdata);
|
||||
|
||||
ArrayList<Icon> imgs = new ArrayList<>();
|
||||
final Icon file1 = Icon.createWithFilePath(originalfile.getAbsolutePath());
|
||||
imgs.add(file1);
|
||||
final Icon bit1 = Icon.createWithBitmap(originalbits);
|
||||
imgs.add(bit1);
|
||||
final Icon data1 = Icon.createWithData(pngdata, 0, pngdata.length);
|
||||
imgs.add(data1);
|
||||
final Icon res1 = Icon.createWithResource(getContext().getResources(), R.drawable.landscape);
|
||||
imgs.add(res1);
|
||||
|
||||
ArrayList<Icon> test = new ArrayList<>();
|
||||
final Parcel parcel = Parcel.obtain();
|
||||
int pos = 0;
|
||||
parcel.writeInt(imgs.size());
|
||||
for (Icon img : imgs) {
|
||||
img.writeToParcel(parcel, 0);
|
||||
L("used %d bytes parceling: %s", parcel.dataPosition() - pos, img);
|
||||
pos = parcel.dataPosition();
|
||||
}
|
||||
|
||||
parcel.setDataPosition(0); // rewind
|
||||
final int N = parcel.readInt();
|
||||
for (int i=0; i<N; i++) {
|
||||
Icon img = Icon.CREATOR.createFromParcel(parcel);
|
||||
L("test %d: read from parcel: %s", i, img);
|
||||
final File testfile = new File(dir,
|
||||
String.format("parcel-test%02d.png", i));
|
||||
|
||||
final Drawable draw1 = img.loadDrawable(mContext);
|
||||
if (draw1 == null) {
|
||||
fail("null drawable from img: " + img);
|
||||
}
|
||||
final Bitmap test1 = Bitmap.createBitmap(draw1.getIntrinsicWidth(),
|
||||
draw1.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||
draw1.setBounds(0, 0, test1.getWidth(), test1.getHeight());
|
||||
draw1.draw(new Canvas(test1));
|
||||
|
||||
try {
|
||||
test1.compress(Bitmap.CompressFormat.PNG, 100,
|
||||
new FileOutputStream(testfile));
|
||||
} catch (java.io.FileNotFoundException ex) {
|
||||
fail("couldn't create test file " + testfile + ": " + ex);
|
||||
}
|
||||
if (!equalBitmaps(originalbits, test1)) {
|
||||
findBitmapDifferences(originalbits, test1);
|
||||
fail(testfile + " differs from original: " + originalfile);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ======== utils ========
|
||||
|
||||
static final char[] GRADIENT = " .:;+=xX$#".toCharArray();
|
||||
static float[] hsv = new float[3];
|
||||
static char colorToChar(int color) {
|
||||
int sum = ((color >> 16) & 0xff)
|
||||
+ ((color >> 8) & 0xff)
|
||||
+ ((color) & 0xff);
|
||||
return GRADIENT[sum * (GRADIENT.length-1) / (3*0xff)];
|
||||
}
|
||||
static void printBits(int[] a, int w, int h) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (int i=0; i<w; i++) {
|
||||
for (int j=0; j<h; j++) {
|
||||
sb.append(colorToChar(a[i+w*j]));
|
||||
}
|
||||
sb.append('\n');
|
||||
}
|
||||
L(sb.toString());
|
||||
}
|
||||
static void printBits(Bitmap a) {
|
||||
final int w = a.getWidth();
|
||||
final int h = a.getHeight();
|
||||
int[] aPix = new int[w * h];
|
||||
printBits(aPix, w, h);
|
||||
}
|
||||
boolean equalBitmaps(Bitmap a, Bitmap b) {
|
||||
if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) return false;
|
||||
|
||||
final int w = a.getWidth();
|
||||
final int h = a.getHeight();
|
||||
int[] aPix = new int[w * h];
|
||||
int[] bPix = new int[w * h];
|
||||
|
||||
a.getPixels(aPix, 0, w, 0, 0, w, h);
|
||||
b.getPixels(bPix, 0, w, 0, 0, w, h);
|
||||
|
||||
return Arrays.equals(aPix, bPix);
|
||||
}
|
||||
|
||||
void findBitmapDifferences(Bitmap a, Bitmap b) {
|
||||
if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) {
|
||||
L("different sizes: %dx%d vs %dx%d",
|
||||
a.getWidth(), a.getHeight(), b.getWidth(), b.getHeight());
|
||||
return;
|
||||
}
|
||||
|
||||
final int w = a.getWidth();
|
||||
final int h = a.getHeight();
|
||||
int[] aPix = new int[w * h];
|
||||
int[] bPix = new int[w * h];
|
||||
|
||||
a.getPixels(aPix, 0, w, 0, 0, w, h);
|
||||
b.getPixels(bPix, 0, w, 0, 0, w, h);
|
||||
|
||||
L("bitmap a (%dx%d)", w, h);
|
||||
printBits(aPix, w, h);
|
||||
L("bitmap b (%dx%d)", w, h);
|
||||
printBits(bPix, w, h);
|
||||
|
||||
StringBuffer sb = new StringBuffer("Different pixels: ");
|
||||
for (int i=0; i<w; i++) {
|
||||
for (int j=0; j<h; j++) {
|
||||
if (aPix[i+w*j] != bPix[i+w*j]) {
|
||||
sb.append(" ").append(i).append(",").append(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
L(sb.toString());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user