/* * Copyright (C) 2017 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 androidx.core.graphics.drawable; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import android.content.Context; import android.content.Intent; import android.content.res.ColorStateList; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.drawable.AdaptiveIconDrawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SdkSuppress; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import androidx.core.content.ContextCompat; import androidx.core.test.R; import org.junit.Test; import org.junit.runner.RunWith; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; @RunWith(AndroidJUnit4.class) @SmallTest public class IconCompatTest { private Context mContext = InstrumentationRegistry.getContext(); private static void verifyClippedCircle(Bitmap bitmap, int fillColor, int size) { assertEquals(size, bitmap.getHeight()); assertEquals(bitmap.getWidth(), bitmap.getHeight()); assertEquals(fillColor, bitmap.getPixel(size / 2, size / 2)); assertEquals(Color.TRANSPARENT, bitmap.getPixel(0, 0)); assertEquals(Color.TRANSPARENT, bitmap.getPixel(0, size - 1)); assertEquals(Color.TRANSPARENT, bitmap.getPixel(size - 1, 0)); // The badge is a full rectangle located at the bottom right corner. Check a single pixel // in that region to verify that badging was properly applied. assertEquals(Color.TRANSPARENT, bitmap.getPixel(size - 1, size - 1)); } public static void verifyBadgeBitmap(Intent intent, int bgColor, int badgeColor) { Bitmap bitmap = intent.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON); int w = bitmap.getWidth(); int h = bitmap.getHeight(); assertEquals(bgColor, bitmap.getPixel(2, 2)); assertEquals(bgColor, bitmap.getPixel(w - 2, 2)); assertEquals(bgColor, bitmap.getPixel(2, h - 2)); assertEquals(badgeColor, bitmap.getPixel(w - 2, h - 2)); } @Test public void testClipAdaptiveIcon() throws Throwable { Bitmap source = Bitmap.createBitmap(200, 150, Bitmap.Config.ARGB_8888); source.eraseColor(Color.RED); Bitmap result = IconCompat.createLegacyIconFromAdaptiveIcon(source, false); verifyClippedCircle(result, Color.RED, 100); } @Test public void testCreateWithBitmap_legacy() { Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888); bitmap.eraseColor(Color.RED); Intent intent = new Intent(); IconCompat.createWithBitmap(bitmap).addToShortcutIntent(intent, null, mContext); assertEquals(bitmap, intent.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON)); } @Test public void testAddBitmapToShortcutIntent_badged() { Context context = InstrumentationRegistry.getContext(); Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888); bitmap.eraseColor(Color.RED); Intent intent = new Intent(); Drawable badge = ContextCompat.getDrawable(context, R.drawable.test_drawable_blue); IconCompat.createWithBitmap(bitmap).addToShortcutIntent(intent, badge, mContext); assertNotSame(bitmap, intent.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON)); verifyBadgeBitmap(intent, Color.RED, ContextCompat.getColor(context, R.color.test_blue)); } @Test public void testAddResourceToShortcutIntent_badged() { Context context = InstrumentationRegistry.getContext(); Intent intent = new Intent(); // No badge IconCompat.createWithResource(context, R.drawable.test_drawable_green) .addToShortcutIntent(intent, null, mContext); assertNotNull(intent.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE)); assertNull(intent.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON)); intent = new Intent(); Drawable badge = ContextCompat.getDrawable(context, R.drawable.test_drawable_red); IconCompat.createWithResource(context, R.drawable.test_drawable_blue) .addToShortcutIntent(intent, badge, mContext); assertNull(intent.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE)); verifyBadgeBitmap(intent, ContextCompat.getColor(context, R.color.test_blue), ContextCompat.getColor(context, R.color.test_red)); } @Test @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M) public void testCreateWithBitmap() { Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888); bitmap.eraseColor(Color.RED); IconCompat compat = IconCompat.createWithBitmap(bitmap); Drawable d = compat.toIcon().loadDrawable(InstrumentationRegistry.getContext()); assertTrue(d instanceof BitmapDrawable); assertEquals(bitmap, ((BitmapDrawable) d).getBitmap()); } @Test public void testCreateWithAdaptiveBitmap_legacy() { Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888); bitmap.eraseColor(Color.GREEN); Intent intent = new Intent(); IconCompat.createWithAdaptiveBitmap(bitmap).addToShortcutIntent(intent, null, mContext); Bitmap clipped = intent.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON); verifyClippedCircle(clipped, Color.GREEN, clipped.getWidth()); } @Test @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M) public void testCreateWithAdaptiveBitmap() { Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888); bitmap.eraseColor(Color.GREEN); IconCompat compat = IconCompat.createWithAdaptiveBitmap(bitmap); Drawable d = compat.toIcon().loadDrawable(InstrumentationRegistry.getContext()); if (Build.VERSION.SDK_INT >= 26) { assertTrue(d instanceof AdaptiveIconDrawable); } else { assertTrue(d instanceof BitmapDrawable); Bitmap clipped = ((BitmapDrawable) d).getBitmap(); verifyClippedCircle(clipped, Color.GREEN, clipped.getWidth()); } } @Test @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M) public void testCreateWithData() { Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888); bitmap.eraseColor(Color.YELLOW); ByteArrayOutputStream out = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); byte[] bytes = out.toByteArray(); byte[] resultCopy = Arrays.copyOf(bytes, bytes.length + 100); // Shift all elements by 20 for (int i = bytes.length - 1; i >= 0; i--) { resultCopy[i + 20] = resultCopy[i]; } IconCompat compat = IconCompat.createWithData(resultCopy, 20, bytes.length); Drawable d = compat.toIcon().loadDrawable(InstrumentationRegistry.getContext()); assertTrue(d instanceof BitmapDrawable); assertTrue(bitmap.sameAs(((BitmapDrawable) d).getBitmap())); } @Test @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M) public void testCreateWithResource() { Context context = InstrumentationRegistry.getContext(); Drawable original = context.getDrawable(R.drawable.test_drawable_red); IconCompat compat = IconCompat.createWithResource(context, R.drawable.test_drawable_red); Drawable d = compat.toIcon().loadDrawable(InstrumentationRegistry.getContext()); // Drawables are same classes assertEquals(original.getClass(), d.getClass()); Bitmap orgBitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888); original.setBounds(0, 0, 200, 200); original.draw(new Canvas(orgBitmap)); Bitmap compatBitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888); d.setBounds(0, 0, 200, 200); d.draw(new Canvas(compatBitmap)); // Drawables behave the same assertTrue(orgBitmap.sameAs(compatBitmap)); } @Test public void testBitmapIconCompat() { verifyIconCompatValidity( IconCompat.createWithBitmap(Bitmap.createBitmap(16, 16, Bitmap.Config.ARGB_8888))); } @Test public void testDataIconCompat() { byte[] data = new byte[4]; data[0] = data[1] = data[2] = data[3] = (byte) 255; verifyIconCompatValidity(IconCompat.createWithData(data, 0, 4)); } @Test public void testFileIconCompat() throws IOException { File file = new File(mContext.getFilesDir(), "testimage.jpg"); try { writeSampleImage(file); assertTrue(file.exists()); verifyIconCompatValidity(IconCompat.createWithContentUri(Uri.fromFile(file))); verifyIconCompatValidity(IconCompat.createWithContentUri(file.toURI().toString())); } finally { file.delete(); } } @Test public void testResourceIconCompat() { verifyIconCompatValidity(IconCompat.createWithResource(mContext, R.drawable.bmp_test)); } @Test public void testBitmapIconCompat_getType() { IconCompat icon = IconCompat.createWithBitmap(Bitmap.createBitmap(16, 16, Bitmap.Config.ARGB_8888)); assertEquals(Icon.TYPE_BITMAP, icon.getType()); } @Test public void testDataIconCompat_getType() { byte[] data = new byte[4]; data[0] = data[1] = data[2] = data[3] = (byte) 255; IconCompat icon = IconCompat.createWithData(data, 0, 4); assertEquals(Icon.TYPE_DATA, icon.getType()); } @Test public void testFileIconCompat_getType() throws IOException { File file = new File(mContext.getFilesDir(), "testimage.jpg"); try { writeSampleImage(file); assertTrue(file.exists()); String filePath = file.toURI().getPath(); IconCompat icon = IconCompat.createWithContentUri(Uri.fromFile(file)); assertEquals(Icon.TYPE_URI, icon.getType()); assertEquals(filePath, icon.getUri().getPath()); icon = IconCompat.createWithContentUri(file.toURI().toString()); assertEquals(Icon.TYPE_URI, icon.getType()); assertEquals(filePath, icon.getUri().getPath()); } finally { file.delete(); } } @Test public void testResourceIconCompat_getType() { IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.bmp_test); assertEquals(Icon.TYPE_RESOURCE, icon.getType()); assertEquals("androidx.core.test", icon.getResPackage()); assertEquals(R.drawable.bmp_test, icon.getResId()); } private void writeSampleImage(File imagefile) throws IOException { try (InputStream source = mContext.getResources().openRawResource(R.drawable.testimage); OutputStream target = new FileOutputStream(imagefile)) { byte[] buffer = new byte[1024]; for (int len = source.read(buffer); len >= 0; len = source.read(buffer)) { target.write(buffer, 0, len); } } } // Check if the created icon is valid and doesn't cause crashes for the public methods. private void verifyIconCompatValidity(IconCompat icon) { assertNotNull(icon); // tint properties. icon.setTint(Color.BLUE); icon.setTintList(ColorStateList.valueOf(Color.RED)); icon.setTintMode(PorterDuff.Mode.XOR); // Parcelable methods. Bundle b = icon.toBundle(); assertNotNull(IconCompat.createFromBundle(b)); // loading drawable synchronously. assertNotNull(icon.loadDrawable(mContext)); } }