Android Espresso match BitmapDrawables with different tint - android

Is there some way to compare two Bitmaps which are wrapped by the BitmapDrawable.
The comparison should not fail if the sizes doesn't match, but it should match pixels and the color of the Bitmap
I am not sure how the native part of Android draws the Bitmap, because sameAs returns true even though the tint color is different.
If the size is different, I can create scaled Bitmap from the other and the compare these to.
In my source code I use DrawableCompat.setTint with the ImageViews Drawable and in the test code I load Drawable from resources and tint it same way.
Any ideas? I would like to have test which validates the source Drawable of ImageView and the color as well based on if it's pressed or not.
NOTE 1: My drawables are white, and I use tint to set color. Looping pixels for Bitmapsdoesn't work because they are white at that point, most likely Android native side uses tint color when drawing.
NOTE 2: Using compile 'com.android.support:palette-v7:21.0.0' and Palette.from(bitmap).generate(); doesn't help either because the returned palette has 0 swatches so can't get any color information there.
This is my current matcher:
public static Matcher<View> withDrawable(final Drawable d) {
return new BoundedMatcher<View, ImageView>(ImageView.class) {
#Override
public boolean matchesSafely(ImageView iv) {
if (d == null) {
return iv.getDrawable() == null;
} else if (iv.getDrawable() == null) {
return false;
}
if (d instanceof BitmapDrawable && iv.getDrawable() instanceof BitmapDrawable) {
BitmapDrawable d1 = (BitmapDrawable) d;
BitmapDrawable d2 = (BitmapDrawable) iv.getDrawable();
Bitmap b1 = d1.getBitmap();
Bitmap b2 = d2.getBitmap();
return b1.sameAs(b2);
}
return iv.getDrawable().getConstantState().equals(d.getConstantState());
}
#Override
public void describeTo(Description description) {
description.appendText("with drawable: ");
}
};
}
Thanks.

I use matchers written by Frankie Sardo for this purpose - https://gist.github.com/frankiesardo/7490059. There are a couple of matchers which must solve your issue. All credits to him.

I've had the opposite issue: matching a drawable when it's size and/or tint can change.
When I convert drawable to bitmap, I use it's default size and apply a black tint:
private fun getBitmapFromDrawable(drawable: Drawable): Bitmap {
val bitmap: Bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.apply {
setBounds(0, 0, canvas.width, canvas.height)
setTint(Color.BLACK)
draw(canvas)
}
return bitmap
}
Usage:
val bitmap = getBitmapFromDrawable(currentDrawable)
val otherBitmap = getBitmapFromDrawable(otherDrawable)
return bitmap.sameAs(otherBitmap)
You could use a new parameter for matching the drawable color...
private fun getBitmapFromDrawable(drawable: Drawable, color:Int): Bitmap {
val bitmap: Bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.apply {
setBounds(0, 0, canvas.width, canvas.height)
setTint(color)
draw(canvas)
}
return bitmap
}
Usage:
val bitmap = getBitmapFromDrawable(currentDrawable, color)
val otherBitmap = getBitmapFromDrawable(otherDrawable, color)
return bitmap.sameAs(otherBitmap)

Related

What is the simplest way to get screenshot in android using kotlin?

I have an imageView and several textViews
My app allows user to drag textViews on evey coordinates of imageView (imageView is not full screen) that user wants .
In other words this app allows user to add several captions to user image
and convert that image and captions to a single image and store it on user device.
According to one of stackOverFlow responses I can just convert one textView text to a bitamp
id there any way to screenshot from final image which user have created with its captions in kotlin??
This is my code:
#Throws(IOException::class)
fun foo(text: String) {
val textPaint = object : Paint() {
init {
setColor(Color.WHITE)
setTextAlign(Align.CENTER)
setTextSize(20f)
setAntiAlias(true)
}
}
val bounds = Rect()
textPaint.getTextBounds(text, 0, text.length, bounds)
val bmp = Bitmap.createBitmap(mImgBanner.getWidth(), mImgBanner.getHeight(), Bitmap.Config.RGB_565) //use ARGB_8888 for better quality
val canvas = Canvas(bmp)
canvas.drawText(text, 0, 20f, textPaint)
val path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/image.png"
val stream = FileOutputStream(path)
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream)
bmp.recycle()
stream.close()
}
Add desired views in xml layout inflate it and take screenshot of parent layout that is containing your views.
Code for taking screenshoot:
fun takeScreenshotOfView(view: View, height: Int, width: Int): Bitmap {
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
val bgDrawable = view.background
if (bgDrawable != null) {
bgDrawable.draw(canvas)
} else {
canvas.drawColor(Color.WHITE)
}
view.draw(canvas)
return bitmap
}
you can also use the extension View.drawToBitmap(). It will return a Bitmap
/**
* Return a [Bitmap] representation of this [View].
*
* The resulting bitmap will be the same width and height as this view's current layout
* dimensions. This does not take into account any transformations such as scale or translation.
*
* Note, this will use the software rendering pipeline to draw the view to the bitmap. This may
* result with different drawing to what is rendered on a hardware accelerated canvas (such as
* the device screen).
*
* If this view has not been laid out this method will throw a [IllegalStateException].
*
* #param config Bitmap config of the desired bitmap. Defaults to [Bitmap.Config.ARGB_8888].
*/
fun View.drawToBitmap(config: Bitmap.Config = Bitmap.Config.ARGB_8888): Bitmap {
if (!ViewCompat.isLaidOut(this)) {
throw IllegalStateException("View needs to be laid out before calling drawToBitmap()")
}
return Bitmap.createBitmap(width, height, config).applyCanvas {
translate(-scrollX.toFloat(), -scrollY.toFloat())
draw(this)
}
}

Using Vector Drawable by drawing in Canvas

I have a imported an SVG into my project as Vector Drawable.
I have my custom view
I know how to display a vector drawable using an ImageView by code (http://www.androidhive.info/2017/02/android-working-svg-vector-drawables/)
, which is described in several articles, but:
How can I draw my vectors using my custom view and by code with canvas ?
Is this possible to do. And if so, can someone give me a hint.
To do this you need to convert the vector to a bitmap as follows:
private fun getVectorBitmap(context: Context, drawableId: Int): Bitmap? {
var bitmap: Bitmap? = null
when (val drawable = ContextCompat.getDrawable(context, drawableId)) {
is BitmapDrawable -> {
bitmap = drawable.bitmap
}
is VectorDrawable -> {
bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight, Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
}
}
return bitmap
}
Then you can use the vector as a bitmap normally with your canvas:
canvas?.drawBitmap(getVectorBitmap(context, R.drawable.ic_icon), 500f, 500f, canvasPaint)
I'm going to turn #pskink's comment into an answer as I'm not so advanced in android as to quickly realize what he meant. I had to read #Krishna's answer to understand. So credits to both of them.
Here it goes:
A vector drawable inherits Drawable that can be drawn directly into a canvas. First, get the drawable from the resources using an android context:
val drawable = yourContext.getDrawable(drawableId)
Then simply draw into the canvas with it. The next line tells the drawable to draw on the top left corner, with a size of 32x32 pixels:
drawable.setBounds(0, 0, 32, 32)
Finally draw it into the canvas:
drawable.draw(canvas)

Chrome Custom Tabs change the default close button not working

I am trying to change the default close button on the actionbar of the custom chrome tabs. I have tried to set using setCloseButtonIcon() However, the default close button still shows. I want to change the close to an arrow.
My code below:
public void openHomePage() {
final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
builder.setToolbarColor(ContextCompat.getColor(getActivity(), R.color.primary));
final Bitmap backButton = BitmapFactory.decodeResource(getResources(), R.drawable.ic_arrow_back_black_48dp);
builder.setCloseButtonIcon(backButton);
builder.setShowTitle(true);
final CustomTabsIntent customTabsIntent = builder.build();
customTabsIntent.launchUrl(getActivity(), Uri.parse(mTvHomepage.getText().toString()));
}
I have an observation. Last month, when searching through SO for various chrome custom tab issues, I found this answer suggesting to use 24dp size icon and also found this question saying that it is working fine with PNG.
I have checked your code with using back arrow icon from here.
When I used "ic_arrow_back_black_48dp", it didn't change the default close button to an arrow (see left image).
final Bitmap backButton = BitmapFactory.decodeResource(getResources(), R.drawable.ic_arrow_back_black_48dp);
But when I used "ic_arrow_back_black_24dp", it perfectly changed the default close button to an arrow (see right image).
final Bitmap backButton = BitmapFactory.decodeResource(getResources(), R.drawable.ic_arrow_back_black_24dp);
As it has worked perfectly for me, you should also try with "24dp" size back arrow icon from here instead of "48dp" size back arrow icon.
Screenshot : [ Device: ASUS_Z00UD; OS: 6.0.1 ]
Presuming you are using the Google library and not on of the related ones the icons size should be 24dp as documented here.
This can be achieved with BitmapFactory Options:
BitmapFactory.Options options = new BitmapFactory.Options();
options.outWidth = 24;
options.outHeight = 24;
options.inScaled = true; //already default, just for illustration - ie scale to screen density (dp)
... = BitmapFactory.decodeResource(getResources(), R.drawable.ic_arrow_back_black_48dp, opts);
You can directly get BitmapDrawable from Drawable but not from VectorDrawable as setCloseButtonIcon requires #NonNull Bitmap icon
You can also use svg as follows. Download the svg from here ic_arrow_back_black_24px
Below methods are self explanatory:
private static Bitmap getBitmapFromDrawable(Context context, int drawableId) {
Drawable drawable = ContextCompat.getDrawable(context, drawableId);
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
} else if (drawable instanceof VectorDrawable) {
return getBitmapFromVectorDrawable((VectorDrawable) drawable);
} else {
throw new IllegalArgumentException("Unable to convert to bitmap");
}
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static Bitmap getBitmapFromVectorDrawable(VectorDrawable vectorDrawable) {
Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(),
vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
vectorDrawable.draw(canvas);
return bitmap;
}
You can use the above as:
builder.setCloseButtonIcon(getBitmapFromDrawable(this, R.drawable.ic_arrow_back_black_24px));
Ref from SO
why not you Add Image Asset and store in mipmap then it will be easier to use default icons inbuilt in android studio
Assest Studio
After Uploading You can easily access icons and image from mipmap in xml file using src resource in ImageView for an instant
android:src="#mipmap/ic_launcher"
To make this work in Kotlin (using Android KTX) with any 24dp drawable resource:
AppCompatResources.getDrawable(activity, R.drawable.ic_arrow_back_white_24dp)?.let {
builder.setCloseButtonIcon(it.toBitmap())
}
And if you need to do some tinting:
AppCompatResources.getDrawable(activity, R.drawable.ic_arrow_back_black_24dp)?.mutate()?.let {
DrawableCompat.setTint(it, Color.WHITE)
builder.setCloseButtonIcon(it.toBitmap())
}
If the drawable needs to be resized, then pass in the new width/height to the Drawable.toBitmap() function.
And if you are not using Kotlin then you can just use the equivalent of the Drawable.toBitmap() code:
fun Drawable.toBitmap(
#Px width: Int = intrinsicWidth,
#Px height: Int = intrinsicHeight,
config: Config? = null
): Bitmap {
if (this is BitmapDrawable) {
if (config == null || bitmap.config == config) {
// Fast-path to return original. Bitmap.createScaledBitmap will do this check, but it
// involves allocation and two jumps into native code so we perform the check ourselves.
if (width == intrinsicWidth && height == intrinsicHeight) {
return bitmap
}
return Bitmap.createScaledBitmap(bitmap, width, height, true)
}
}
val (oldLeft, oldTop, oldRight, oldBottom) = bounds
val bitmap = Bitmap.createBitmap(width, height, config ?: Config.ARGB_8888)
setBounds(0, 0, width, height)
draw(Canvas(bitmap))
setBounds(oldLeft, oldTop, oldRight, oldBottom)
return bitmap
}
For more see this answer.
I too had to face the same problem
Solution :-
1) Take image(back arrow) in png format.
2) Keep the image size under 24dp .
Although this question has already been answered, since I had this problem too and none of the above suggestions solved my Problem I want to share the way I solved my problem. Hope it helps others.
Ref
fun Context.openCustomTab(
url: String,
#ColorRes toolbarColor: Int,
#ColorRes iconColor: Int,
#DrawableRes drawable: Int
) {
CustomTabsIntent.Builder().let { ctb ->
AppCompatResources.getDrawable(this, drawable)?.let {
DrawableCompat.setTint(it, resources.getColor(iconColor))
ctb.setCloseButtonIcon(it.toBitmap())
}
ctb.setDefaultColorSchemeParams(
CustomTabColorSchemeParams.Builder()
.setToolbarColor(
ContextCompat.getColor(
this,
toolbarColor,
)
).build()
)
ctb.setShowTitle(true)
}.build().launchUrl(this, Uri.parse(url))
}

setCloseButtonIcon(Bitmap drawable) is not working with SVGs in ChromeCustomTab

I need to change the default cross-icon in ChromeCustomTab Android, I am changing it with back-icon using the code below:
Bitmap icon = BitmapFactory.decodeResource(context.getResources(),R.drawable.ic_arrow_back_white_24dp);
It is working fine with PNGs but not with SVGs.
As per this documentation, we have to maintain size of image according to this documentation.
https://developer.android.com/reference/android/support/customtabs/CustomTabsIntent.html#KEY_ICON
I think it is not working because they are not following the dimensions given in Documentation.
You need to return a valid Bitmap. For a VectorDrawable it is necessary to do something more. You can use these methods:
private static Bitmap bitmapFromDrawable(Context context, int drawableId) {
Drawable drawable = ContextCompat.getDrawable(context, drawableId);
if (drawable instanceof VectorDrawable) {
return bitmapFromVectorDrawable((VectorDrawable) drawable);
}
return ((BitmapDrawable) drawable).getBitmap();
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static Bitmap bitmapFromVectorDrawable(VectorDrawable vectorDrawable) {
Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
vectorDrawable.draw(canvas);
return bitmap;
}
Then you can use it like:
builder.setCloseButtonIcon(bitmapFromDrawable(this, R.drawable. ic_arrow_back_white_24dp));

How do i convert to Color to Bitmap?

I have a color in form of integer, and i want that color to be in a Bitmap form.
Is there any way to do so?
I tried
Drawable d = new ColorDrawable(Color.parseColor("#ffffff"));
Bitmap b = ((BitmapDrawable)d).getbitmap();
But the above code give error Cannot cast ColorDrawable to BitmapDrawable
Is there any other way?
Actual code is
Palette.generateAsync(BitmapFactory.decodeFile(songArt),
new Palette.PaletteAsyncListener() {
#Override
public void onGenerated(final Palette palette) {
if (Build.VERSION.SDK_INT >= 16) {
Drawable colorDrawable = new ColorDrawable(palette.getDarkVibrantColor(
getResources().getColor(R.color.noti_background)));
notificationCompat.bigContentView.setImageViewResource(R.id.noti_color_bg,
((BitmapDrawable) colorDrawable).getBitmap());
notificationManager.notify(NOTIFICATION_ID, notificationCompat);
}
}
}
);
Yes there is.
You can just do it like this:
Bitmap bmp = Bitmap.createBitmap(width, height, Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);
canvas.drawColor(colorInt)
Inside drawColor() you can also set the color by using the methods of the Color class like Color.argb(...) or Color.rgb(...)
That way you will have a bitmap with dimensions width/height and filled with the specified color.
Further explanation: You create a Bitmap object with your specified dimensions. Then you create a Canvas object and attach the Bitmap object to it. This way everything that is drawn using the Canvas object methods gets drawn on the Bitmap object.
In the end you have a Bitmap object with your drawings in it.

Categories

Resources